]>
Commit | Line | Data |
---|---|---|
56b69519 | 1 | /* $Id: ipcache.cc,v 1.7 1996/03/22 20:58:11 wessels Exp $ */ |
ed43818f | 2 | |
090089c4 | 3 | #include "config.h" |
4 | #include <sys/types.h> | |
5 | #include <sys/stat.h> | |
6 | #include <sys/socket.h> | |
7 | #include <stdlib.h> | |
8 | #include <unistd.h> | |
9 | #include <string.h> | |
10 | #include <netdb.h> | |
11 | #include <memory.h> | |
12 | #include <fcntl.h> | |
13 | #include <errno.h> | |
14 | ||
15 | #include <time.h> | |
16 | #include <sys/time.h> | |
17 | #include <netinet/in.h> | |
18 | #include <sys/un.h> | |
19 | ||
20 | #include "ansihelp.h" /* goes first */ | |
21 | #include "debug.h" | |
22 | #include "comm.h" | |
23 | #include "fdstat.h" | |
24 | #include "icp.h" | |
25 | #include "cache_cf.h" | |
26 | #include "ipcache.h" | |
27 | #include "autoconf.h" | |
28 | #include "stat.h" | |
29 | #include "hash.h" | |
644642ab | 30 | #include "disk.h" |
090089c4 | 31 | #include "util.h" |
32 | ||
33 | struct hostent *gethostbyname(); | |
34 | int urlcmp _PARAMS((char *s1, char *s2)); | |
35 | ||
36 | #define MAX_LINELEN (4096) | |
37 | char ipcache_status_char _PARAMS((ipcache_entry *)); | |
38 | int ipcache_hash_entry_count(); | |
39 | ||
40 | #define IP_POS_TTL 86400 /* one day */ | |
41 | #define IP_NEG_TTL 120 /* 2 minutes */ | |
42 | #define MAX_IP 1024 /* Maximum cached IP */ | |
43 | #define IP_LOW_WATER 70 | |
44 | #define IP_HIGH_WATER 90 | |
45 | #define MAX_HOST_NAME 256 | |
46 | #define IP_INBUF 4096 | |
47 | ||
48 | long ipcache_low = 180; | |
49 | long ipcache_high = 200; | |
50 | ||
51 | typedef struct _ip_pending { | |
52 | int fd; | |
53 | IPH handler; | |
54 | caddr_t data; | |
55 | struct _ip_pending *next; | |
56 | } IpPending; | |
57 | ||
58 | ||
59 | typedef struct _ipcache_list { | |
60 | ipcache_entry *entry; | |
61 | struct _ipcache_list *next; | |
62 | } ipcache_list; | |
63 | ||
64 | ||
65 | typedef struct _dnsserver_entry { | |
66 | int id; | |
67 | int alive; | |
68 | int inpipe; | |
69 | int outpipe; | |
70 | int pending_count; /* counter of outstanding request */ | |
71 | long lastcall; | |
72 | long answer; | |
73 | unsigned int offset; | |
74 | unsigned int size; | |
75 | char *ip_inbuf; | |
76 | /* global ipcache_entry list for pending entry */ | |
77 | ipcache_list *global_pending; | |
78 | ipcache_list *global_pending_tail; | |
79 | } dnsserver_entry; | |
80 | ||
81 | typedef struct _line_entry { | |
82 | char *line; | |
83 | struct _line_entry *next; | |
84 | } line_entry; | |
85 | ||
86 | #define TEST_SITE 5 | |
87 | static char *test_site[TEST_SITE] = | |
88 | { | |
89 | "internic.net", | |
90 | "usc.edu", | |
91 | "cs.colorado.edu", | |
92 | "mit.edu", | |
93 | "yale.edu" | |
94 | }; | |
95 | ||
96 | static char w_space[] = " \t\n"; | |
97 | static dnsserver_entry **dns_child_table = NULL; | |
98 | static int last_dns_dispatched = 2; | |
99 | static struct hostent *static_result = NULL; | |
090089c4 | 100 | static int dns_child_alive = 0; |
101 | static int ipcache_initialized = 0; | |
102 | ||
103 | char *dns_error_message = NULL; /* possible error message */ | |
104 | HashID ip_table = 0; | |
105 | ||
106 | extern int do_dns_test; | |
107 | extern time_t cached_curtime; | |
108 | extern int getMaxFD(); | |
109 | extern int getDnsChildren(); | |
110 | extern void fatal_dump _PARAMS((char *)); | |
1ac90c59 | 111 | extern int file_update_open _PARAMS((int, char *)); |
090089c4 | 112 | |
113 | void update_dns_child_alive() | |
114 | { | |
115 | int i; | |
116 | ||
117 | dns_child_alive = 0; | |
118 | for (i = 0; i < getDnsChildren(); ++i) { | |
119 | if (dns_child_table[i]->alive) { | |
120 | dns_child_alive = 1; | |
121 | break; | |
122 | } | |
123 | } | |
124 | } | |
125 | ||
126 | int ipcache_testname() | |
127 | { | |
128 | int success, i; | |
129 | ||
130 | for (success = i = 0; i < TEST_SITE; i++) { | |
131 | if (gethostbyname(test_site[i]) != NULL) | |
132 | ++success; | |
133 | } | |
134 | return (success == 0) ? -1 : 0; | |
135 | } | |
136 | ||
137 | ||
090089c4 | 138 | |
139 | ||
140 | /* | |
141 | * open a UNIX domain socket for rendevouing with dnsservers | |
142 | */ | |
143 | int ipcache_create_dnsserver(command) | |
144 | char *command; | |
145 | { | |
146 | int pid; | |
147 | struct sockaddr_un addr; | |
148 | static int n_dnsserver = 0; | |
149 | char socketname[256]; | |
150 | int cfd; /* socket for child (dnsserver) */ | |
151 | int sfd; /* socket for server (cached) */ | |
152 | int fd; | |
153 | ||
154 | if ((cfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { | |
155 | debug(0, "ipcache_create_dnsserver: socket: %s\n", xstrerror()); | |
156 | return -1; | |
157 | } | |
56b69519 | 158 | fdstat_open(cfd, Socket); |
159 | fd_note(cfd, "socket to dnsserver"); | |
090089c4 | 160 | memset(&addr, '\0', sizeof(addr)); |
161 | addr.sun_family = AF_UNIX; | |
162 | sprintf(socketname, "dns/dns%d.%d", (int) getpid(), n_dnsserver++); | |
163 | strcpy(addr.sun_path, socketname); | |
164 | debug(4, "ipcache_create_dnsserver: path is %s\n", addr.sun_path); | |
165 | ||
166 | if (bind(cfd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { | |
167 | close(cfd); | |
168 | debug(0, "ipcache_create_dnsserver: bind: %s\n", xstrerror()); | |
169 | return -1; | |
170 | } | |
171 | debug(4, "ipcache_create_dnsserver: bind to local host.\n"); | |
172 | listen(cfd, 1); | |
173 | ||
174 | if ((pid = fork()) < 0) { | |
175 | debug(0, "ipcache_create_dnsserver: fork: %s\n", xstrerror()); | |
176 | close(cfd); | |
177 | return -1; | |
178 | } | |
179 | if (pid > 0) { /* parent */ | |
180 | close(cfd); /* close shared socket with child */ | |
181 | ||
182 | /* open new socket for parent process */ | |
183 | if ((sfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { | |
184 | debug(0, "ipcache_create_dnsserver: socket: %s\n", xstrerror()); | |
185 | return -1; | |
186 | } | |
187 | fcntl(sfd, F_SETFD, 1); /* set close-on-exec */ | |
188 | memset(&addr, '\0', sizeof(addr)); | |
189 | addr.sun_family = AF_UNIX; | |
190 | strcpy(addr.sun_path, socketname); | |
191 | if (connect(sfd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { | |
192 | close(sfd); | |
193 | debug(0, "ipcache_create_dnsserver: connect: %s\n", xstrerror()); | |
194 | return -1; | |
195 | } | |
196 | debug(4, "ipcache_create_dnsserver: FD %d connected to %s #%d.\n", | |
197 | sfd, command, n_dnsserver); | |
198 | return sfd; | |
199 | } | |
200 | /* child */ | |
201 | ||
202 | dup2(cfd, 3); | |
56b69519 | 203 | for (fd = getMaxFD(); fd > 3; fd--) { |
090089c4 | 204 | (void) close(fd); |
205 | } | |
206 | ||
207 | execlp(command, "(dnsserver)", "-p", socketname, NULL); | |
208 | perror(command); | |
209 | _exit(1); | |
52e1d7e2 | 210 | return (0); /* NOTREACHED */ |
090089c4 | 211 | } |
212 | ||
090089c4 | 213 | |
214 | /* removes the given ipcache entry */ | |
215 | int ipcache_release(e) | |
216 | ipcache_entry *e; | |
217 | { | |
218 | ipcache_entry *result = 0; | |
219 | int i; | |
220 | ||
221 | debug(5, "ipcache_release: ipcache_count before: %d \n", meta_data.ipcache_count); | |
222 | ||
223 | if (e != NULL && ip_table) { /* sometimes called with NULL e */ | |
224 | hash_link *table_entry = hash_lookup(ip_table, e->name); | |
225 | if (table_entry) { | |
226 | result = (ipcache_entry *) table_entry; | |
227 | debug(5, "HASH table count before delete: %d\n", ipcache_hash_entry_count()); | |
228 | if (hash_remove_link(ip_table, table_entry)) { | |
229 | debug(3, "ipcache_release: Cannot delete '%s' from hash table %d\n", e->name, ip_table); | |
230 | } | |
231 | debug(5, "HASH table count after delete: %d\n", ipcache_hash_entry_count()); | |
232 | if (result) { | |
233 | if (result->status == PENDING) { | |
234 | debug(1, "ipcache_release: Try to release entry with PENDING status. ignored.\n"); | |
235 | debug(5, "ipcache_release: ipcache_count: %d \n", meta_data.ipcache_count); | |
236 | return -1; | |
237 | } | |
238 | if (result->status == CACHED) { | |
239 | if (result->addr_count) | |
240 | for (i = 0; i < (int) result->addr_count; i++) | |
241 | safe_free(result->entry.h_addr_list[i]); | |
242 | if (result->entry.h_addr_list) | |
243 | safe_free(result->entry.h_addr_list); | |
244 | if (result->alias_count) | |
245 | for (i = 0; i < (int) result->alias_count; i++) | |
246 | safe_free(result->entry.h_aliases[i]); | |
247 | if (result->entry.h_aliases) | |
248 | safe_free(result->entry.h_aliases); | |
249 | safe_free(result->entry.h_name); | |
250 | debug(5, "ipcache_release: Released IP cached record for '%s'.\n", e->name); | |
251 | } | |
252 | /* XXX: we're having mem mgmt problems; zero, then free */ | |
253 | safe_free(result->name); | |
254 | memset(result, '\0', sizeof(ipcache_entry)); | |
255 | safe_free(result); | |
256 | } | |
257 | --meta_data.ipcache_count; | |
258 | debug(5, "ipcache_release: ipcache_count when return: %d \n", meta_data.ipcache_count); | |
259 | return meta_data.ipcache_count; | |
260 | } | |
261 | } | |
262 | debug(3, "ipcache_release: can't delete entry\n"); | |
263 | return -1; /* can't delete entry */ | |
264 | } | |
265 | ||
266 | /* return match for given name */ | |
267 | ipcache_entry *ipcache_get(name) | |
268 | char *name; | |
269 | { | |
270 | hash_link *e; | |
271 | static ipcache_entry *result; | |
272 | ||
273 | result = NULL; | |
274 | if (ip_table) { | |
275 | if ((e = hash_lookup(ip_table, name)) != NULL) | |
276 | result = (ipcache_entry *) e; | |
277 | } | |
278 | if (result == NULL) | |
279 | return NULL; | |
280 | ||
281 | if (((result->timestamp + result->ttl) < cached_curtime) && | |
282 | (result->status != PENDING)) { /* expired? */ | |
283 | ipcache_release(result); | |
284 | return NULL; | |
285 | } | |
286 | return result; | |
287 | } | |
288 | ||
289 | ||
290 | /* get the first ip entry in the storage */ | |
291 | ipcache_entry *ipcache_GetFirst() | |
292 | { | |
293 | static hash_link *entryPtr; | |
294 | ||
295 | if ((!ip_table) || ((entryPtr = hash_first(ip_table)) == NULL)) | |
296 | return NULL; | |
297 | return ((ipcache_entry *) entryPtr); | |
298 | } | |
299 | ||
300 | ||
301 | /* get the next ip entry in the storage for a given search pointer */ | |
302 | ipcache_entry *ipcache_GetNext() | |
303 | { | |
304 | static hash_link *entryPtr; | |
305 | ||
306 | if ((!ip_table) || ((entryPtr = hash_next(ip_table)) == NULL)) | |
307 | return NULL; | |
308 | return ((ipcache_entry *) entryPtr); | |
309 | } | |
310 | ||
311 | int ipcache_compareLastRef(e1, e2) | |
312 | ipcache_entry **e1, **e2; | |
313 | { | |
314 | if (!e1 || !e2) | |
315 | fatal_dump(NULL); | |
316 | ||
317 | if ((*e1)->lastref > (*e2)->lastref) | |
318 | return (1); | |
319 | ||
320 | if ((*e1)->lastref < (*e2)->lastref) | |
321 | return (-1); | |
322 | ||
323 | return (0); | |
324 | } | |
325 | ||
326 | ||
327 | ||
328 | /* finds the LRU and deletes */ | |
329 | int ipcache_purgelru() | |
330 | { | |
331 | ipcache_entry *e; | |
332 | int local_ip_count = 0; | |
333 | int local_ip_notpending_count = 0; | |
334 | int removed = 0; | |
335 | int i; | |
336 | ipcache_entry **LRU_list; | |
337 | int LRU_list_count = 0; | |
338 | int LRU_cur_size = meta_data.ipcache_count; | |
339 | ||
340 | LRU_list = (ipcache_entry **) xcalloc(LRU_cur_size, sizeof(ipcache_entry *)); | |
341 | ||
342 | e = NULL; | |
343 | ||
344 | for (e = ipcache_GetFirst(); e; e = ipcache_GetNext()) { | |
345 | local_ip_count++; | |
346 | ||
347 | if (LRU_list_count >= LRU_cur_size) { | |
348 | /* have to realloc */ | |
349 | LRU_cur_size += 16; | |
350 | debug(3, "ipcache_purgelru: Have to grow LRU_list to %d. This shouldn't happen.\n", | |
351 | LRU_cur_size); | |
352 | LRU_list = (ipcache_entry **) xrealloc((char *) LRU_list, | |
353 | LRU_cur_size * sizeof(ipcache_entry *)); | |
354 | } | |
355 | if ((e->status != PENDING) && (e->pending_head == NULL)) { | |
356 | local_ip_notpending_count++; | |
357 | LRU_list[LRU_list_count++] = e; | |
358 | } | |
359 | } | |
360 | ||
361 | debug(3, "ipcache_purgelru: ipcache_count: %5d\n", meta_data.ipcache_count); | |
362 | debug(3, " actual count : %5d\n", local_ip_count); | |
363 | debug(3, " high W mark : %5d\n", ipcache_high); | |
364 | debug(3, " low W mark : %5d\n", ipcache_low); | |
365 | debug(3, " not pending : %5d\n", local_ip_notpending_count); | |
366 | debug(3, " LRU candidated : %5d\n", LRU_list_count); | |
367 | ||
368 | /* sort LRU candidate list */ | |
369 | qsort((char *) LRU_list, LRU_list_count, sizeof(e), (int (*)(const void *, const void *)) ipcache_compareLastRef); | |
370 | ||
371 | for (i = 0; LRU_list[i] && (meta_data.ipcache_count > ipcache_low) | |
372 | && i < LRU_list_count; | |
373 | ++i) { | |
374 | ipcache_release(LRU_list[i]); | |
375 | removed++; | |
376 | } | |
377 | ||
378 | debug(3, " removed : %5d\n", removed); | |
379 | safe_free(LRU_list); | |
380 | return (removed > 0) ? 0 : -1; | |
381 | } | |
382 | ||
383 | ||
384 | /* create blank ipcache_entry */ | |
385 | ipcache_entry *ipcache_create() | |
386 | { | |
387 | static ipcache_entry *ipe; | |
388 | static ipcache_entry *new; | |
389 | debug(5, "ipcache_create: when enter. ipcache_count == %d\n", meta_data.ipcache_count); | |
390 | ||
391 | if (meta_data.ipcache_count > ipcache_high) { | |
392 | if (ipcache_purgelru() < 0) { | |
393 | debug(1, "ipcache_create: Cannot release needed IP entry via LRU: %d > %d, removing first entry...\n", meta_data.ipcache_count, MAX_IP); | |
394 | ipe = ipcache_GetFirst(); | |
395 | if (!ipe) { | |
396 | debug(1, "ipcache_create: First entry is a null pointer ???\n"); | |
397 | /* have to let it grow beyond limit here */ | |
398 | } else if (ipe && ipe->status != PENDING) { | |
399 | ipcache_release(ipe); | |
400 | } else { | |
401 | debug(1, "ipcache_create: First entry is also PENDING entry.\n"); | |
402 | /* have to let it grow beyond limit here */ | |
403 | } | |
404 | } | |
405 | } | |
406 | meta_data.ipcache_count++; | |
407 | debug(5, "ipcache_create: before return. ipcache_count == %d\n", meta_data.ipcache_count); | |
408 | new = (ipcache_entry *) xcalloc(1, sizeof(ipcache_entry)); | |
409 | /* set default to 4, in case parser fail to get token $h_length from | |
410 | * dnsserver. */ | |
411 | new->entry.h_length = 4; | |
412 | return new; | |
413 | ||
414 | } | |
415 | ||
416 | void ipcache_add_to_hash(e) | |
417 | ipcache_entry *e; | |
418 | { | |
419 | if (!ipcache_initialized) | |
420 | ipcache_init(); | |
421 | if (hash_join(ip_table, (hash_link *) e)) { | |
422 | debug(1, "ipcache_add_to_hash: Cannot add %s (%p) to hash table %d.\n", | |
423 | e->name, e, ip_table); | |
424 | } | |
425 | debug(5, "ipcache_add_to_hash: name <%s>\n", e->name); | |
426 | debug(5, " ipcache_count: %d\n", meta_data.ipcache_count); | |
427 | } | |
428 | ||
429 | ||
430 | void ipcache_add(name, e, data, cached) | |
431 | char *name; | |
432 | ipcache_entry *e; | |
433 | struct hostent *data; | |
434 | int cached; | |
435 | { | |
436 | int addr_count, alias_count, i; | |
437 | ||
438 | debug(10, "ipcache_add: Adding name '%s' (%s).\n", name, | |
439 | cached ? "cached" : "not cached"); | |
440 | ||
441 | e->name = xstrdup(name); | |
442 | if (cached) { | |
443 | ||
444 | /* count for IPs */ | |
445 | addr_count = 0; | |
446 | while ((addr_count < 255) && data->h_addr_list[addr_count]) | |
447 | ++addr_count; | |
448 | ||
449 | e->addr_count = addr_count; | |
450 | ||
451 | /* count for Alias */ | |
452 | alias_count = 0; | |
453 | if (data->h_aliases) | |
454 | while ((alias_count < 255) && data->h_aliases[alias_count]) | |
455 | ++alias_count; | |
456 | ||
457 | e->alias_count = alias_count; | |
458 | ||
459 | /* copy ip addresses information */ | |
460 | e->entry.h_addr_list = (char **) xcalloc(addr_count + 1, sizeof(char *)); | |
461 | for (i = 0; i < addr_count; i++) { | |
462 | e->entry.h_addr_list[i] = (char *) xcalloc(1, data->h_length); | |
463 | memcpy(e->entry.h_addr_list[i], data->h_addr_list[i], data->h_length); | |
464 | } | |
465 | ||
466 | if (alias_count) { | |
467 | /* copy aliases information */ | |
468 | e->entry.h_aliases = (char **) xcalloc(alias_count + 1, sizeof(char *)); | |
469 | for (i = 0; i < alias_count; i++) { | |
470 | e->entry.h_aliases[i] = (char *) xcalloc(1, strlen(data->h_aliases[i]) + 1); | |
471 | strcpy(e->entry.h_aliases[i], data->h_aliases[i]); | |
472 | } | |
473 | } | |
474 | e->entry.h_length = data->h_length; | |
475 | e->entry.h_name = xstrdup(data->h_name); | |
476 | e->lastref = e->timestamp = cached_curtime; | |
477 | e->status = CACHED; | |
478 | e->ttl = IP_POS_TTL; | |
479 | } else { | |
480 | e->lastref = e->timestamp = cached_curtime; | |
481 | e->status = NEGATIVE_CACHED; | |
482 | e->ttl = IP_NEG_TTL; | |
483 | } | |
484 | ||
485 | ipcache_add_to_hash(e); | |
486 | } | |
487 | ||
488 | ||
489 | /* exactly the same to ipcache_add, | |
490 | * except it does NOT | |
491 | * - create entry->name (assume it's there already.) | |
492 | * - add the entry to the hash (it's should be in hash table already.). | |
493 | * | |
494 | * Intend to be used by ipcache_cleanup_pendinglist. | |
495 | */ | |
496 | void ipcache_update_content(name, e, data, cached) | |
497 | char *name; | |
498 | ipcache_entry *e; | |
499 | struct hostent *data; | |
500 | int cached; | |
501 | { | |
502 | int addr_count, alias_count, i; | |
503 | ||
504 | debug(10, "ipcache_update: Updating name '%s' (%s).\n", name, | |
505 | cached ? "cached" : "not cached"); | |
506 | ||
507 | if (cached) { | |
508 | ||
509 | /* count for IPs */ | |
510 | addr_count = 0; | |
511 | while ((addr_count < 255) && data->h_addr_list[addr_count]) | |
512 | ++addr_count; | |
513 | ||
514 | e->addr_count = addr_count; | |
515 | ||
516 | /* count for Alias */ | |
517 | alias_count = 0; | |
518 | while ((alias_count < 255) && data->h_aliases[alias_count]) | |
519 | ++alias_count; | |
520 | ||
521 | e->alias_count = alias_count; | |
522 | ||
523 | /* copy ip addresses information */ | |
524 | e->entry.h_addr_list = (char **) xcalloc(addr_count + 1, sizeof(char *)); | |
525 | for (i = 0; i < addr_count; i++) { | |
526 | e->entry.h_addr_list[i] = (char *) xcalloc(1, data->h_length); | |
527 | memcpy(e->entry.h_addr_list[i], data->h_addr_list[i], data->h_length); | |
528 | } | |
529 | ||
530 | /* copy aliases information */ | |
531 | e->entry.h_aliases = (char **) xcalloc(alias_count + 1, sizeof(char *)); | |
532 | for (i = 0; i < alias_count; i++) { | |
533 | e->entry.h_aliases[i] = (char *) xcalloc(1, strlen(data->h_aliases[i]) + 1); | |
534 | strcpy(e->entry.h_aliases[i], data->h_aliases[i]); | |
535 | } | |
536 | ||
537 | e->entry.h_length = data->h_length; | |
538 | e->entry.h_name = xstrdup(data->h_name); | |
539 | e->lastref = e->timestamp = cached_curtime; | |
540 | e->status = CACHED; | |
541 | e->ttl = IP_POS_TTL; | |
542 | } else { | |
543 | e->lastref = e->timestamp = cached_curtime; | |
544 | e->status = NEGATIVE_CACHED; | |
545 | e->ttl = IP_NEG_TTL; | |
546 | } | |
547 | ||
548 | } | |
549 | ||
550 | ||
551 | ||
552 | /* walks down the pending list, calling handlers */ | |
553 | void ipcache_call_pending(entry) | |
554 | ipcache_entry *entry; | |
555 | { | |
556 | IpPending *p; | |
557 | int nhandler = 0; | |
558 | ||
559 | entry->lastref = cached_curtime; | |
560 | ||
561 | while (entry->pending_head != NULL) { | |
562 | p = entry->pending_head; | |
563 | entry->pending_head = entry->pending_head->next; | |
564 | if (entry->pending_head == NULL) | |
565 | entry->pending_tail = NULL; | |
566 | if (p->handler != NULL) { | |
567 | nhandler++; | |
568 | p->handler(p->fd, (entry->status == CACHED) ? | |
569 | &(entry->entry) : NULL, p->data); | |
570 | } | |
571 | memset(p, '\0', sizeof(IpPending)); | |
572 | safe_free(p); | |
573 | } | |
574 | entry->pending_head = entry->pending_tail = NULL; /* nuke list */ | |
575 | debug(10, "ipcache_call_pending: Called %d handlers.\n", nhandler); | |
576 | } | |
577 | ||
578 | void ipcache_call_pending_badname(fd, handler, data) | |
579 | int fd; | |
580 | IPH handler; | |
581 | caddr_t data; | |
582 | { | |
583 | debug(4, "ipcache_call_pending_badname: Bad Name: Calling handler with NULL result.\n"); | |
584 | handler(fd, NULL, data); | |
585 | } | |
586 | ||
587 | ||
588 | /* call when dnsserver is broken, have to switch to blocking mode. | |
589 | * All pending lookup will be looked up by blocking call. | |
590 | */ | |
591 | int ipcache_cleanup_pendinglist(data) | |
592 | dnsserver_entry *data; | |
593 | { | |
594 | ipcache_list *p; | |
595 | struct hostent *s_result = NULL; | |
596 | ||
597 | while (data->global_pending != NULL) { | |
598 | s_result = gethostbyname(data->global_pending->entry->name); | |
599 | ipcache_update_content(data->global_pending->entry->name, | |
600 | data->global_pending->entry, s_result, s_result ? 1 : 0); | |
601 | ipcache_call_pending(data->global_pending->entry); | |
602 | p = data->global_pending; | |
603 | data->global_pending = data->global_pending->next; | |
604 | /* XXX: we're having mem mgmt problems; zero, then free */ | |
605 | memset(p, '\0', sizeof(ipcache_list)); | |
606 | safe_free(p); | |
607 | } | |
608 | data->global_pending = data->global_pending_tail = NULL; /* nuke */ | |
609 | return 0; | |
610 | } | |
611 | ||
612 | /* free all lines in the list */ | |
613 | void free_lines(line) | |
614 | line_entry *line; | |
615 | { | |
616 | line_entry *tmp; | |
617 | ||
618 | while (line) { | |
619 | tmp = line; | |
620 | line = line->next; | |
621 | safe_free(tmp->line); | |
622 | safe_free(tmp); | |
623 | } | |
624 | } | |
625 | ||
626 | /* return entry in global pending list that has entry which key match to name */ | |
627 | ipcache_list *globalpending_search(name, global_pending) | |
628 | char *name; | |
629 | ipcache_list *global_pending; | |
630 | { | |
631 | static ipcache_list *p; | |
632 | ||
633 | if (name == NULL) | |
634 | return NULL; | |
635 | ||
636 | for (p = global_pending; p != NULL; p = p->next) { | |
637 | /* XXX: this is causing core dumps! p->entry is corrupt */ | |
638 | if (p->entry && p->entry->name && | |
639 | strcmp(p->entry->name, name) == 0) { | |
640 | return p; | |
641 | } | |
642 | } | |
643 | return NULL; | |
644 | ||
645 | } | |
646 | ||
647 | /* remove entry from global pending list */ | |
648 | void globalpending_remove(p, data) | |
649 | ipcache_list *p; | |
650 | dnsserver_entry *data; | |
651 | { | |
652 | ipcache_list *q, *r; | |
653 | ||
654 | r = q = data->global_pending; | |
655 | while (q && (p != q)) { | |
656 | r = q; /* r is the node before the one to kill */ | |
657 | q = q->next; /* q (and 'p') is the node to kill */ | |
658 | } | |
659 | ||
660 | if (q == NULL) { /* 'p' is not in the list? */ | |
661 | debug(1, "globalpending_remove: Failure while deleting entry from global pending list.\n"); | |
662 | return; | |
663 | } | |
664 | /* nuke p from the list; do this carefully... */ | |
665 | if (p == data->global_pending) { /* p is head */ | |
666 | if (p->next != NULL) { /* nuke head */ | |
667 | data->global_pending = p->next; | |
668 | } else { /* nuke whole list */ | |
669 | data->global_pending = NULL; | |
670 | data->global_pending_tail = NULL; | |
671 | } | |
672 | } else if (p == data->global_pending_tail) { /* p is tail */ | |
673 | data->global_pending_tail = r; /* tail is prev */ | |
674 | data->global_pending_tail->next = NULL; /* last node */ | |
675 | } else { /* p in middle */ | |
676 | r->next = p->next; | |
677 | } | |
678 | ||
679 | /* we need to delete all references to p */ | |
680 | /* XXX: we're having mem mgmt probs; zero then free DRH */ | |
681 | memset(p, '\0', sizeof(ipcache_list)); | |
682 | /* XXX: what about freeing p->entry? DRH */ | |
683 | safe_free(p); | |
684 | ||
685 | if (data->pending_count > 0) | |
686 | data->pending_count--; | |
687 | ||
688 | } | |
689 | ||
690 | /* scan through buffer and do a conversion if possible | |
691 | * return number of char used */ | |
692 | int ipcache_parsebuffer(buf, offset, data) | |
693 | char *buf; | |
694 | unsigned int offset; | |
695 | dnsserver_entry *data; | |
696 | { | |
697 | char *pos = NULL; | |
698 | char *tpos = NULL; | |
699 | char *endpos = NULL; | |
700 | char *token = NULL; | |
701 | char *tmp_ptr = NULL; | |
702 | line_entry *line_head = NULL; | |
703 | line_entry *line_tail = NULL; | |
704 | line_entry *line_cur = NULL; | |
705 | ipcache_list *plist = NULL; | |
706 | ||
707 | *dns_error_message = '\0'; | |
708 | ||
709 | pos = buf; | |
710 | while (pos < (buf + offset)) { | |
711 | ||
712 | /* no complete record here */ | |
713 | if ((endpos = strstr(pos, "$end\n")) == NULL) { | |
714 | debug(2, "ipcache_parsebuffer: DNS response incomplete.\n"); | |
715 | break; | |
716 | } | |
717 | line_head = line_tail = NULL; | |
718 | ||
719 | while (pos < endpos) { | |
720 | /* add the next line to the end of the list */ | |
721 | line_cur = (line_entry *) xcalloc(1, sizeof(line_entry)); | |
722 | ||
723 | if ((tpos = memchr(pos, '\n', 4096)) == NULL) { | |
724 | debug(2, "ipcache_parsebuffer: DNS response incomplete.\n"); | |
725 | return -1; | |
726 | } | |
727 | *tpos = '\0'; | |
728 | line_cur->line = xstrdup(pos); | |
729 | debug(7, "ipcache_parsebuffer: %s\n", line_cur->line); | |
730 | *tpos = '\n'; | |
731 | ||
732 | if (line_tail) | |
733 | line_tail->next = line_cur; | |
734 | if (line_head == NULL) | |
735 | line_head = line_cur; | |
736 | line_tail = line_cur; | |
737 | line_cur = NULL; | |
738 | ||
739 | /* update pointer */ | |
740 | pos = tpos + 1; | |
741 | } | |
742 | pos = endpos + 5; /* strlen("$end\n") */ | |
743 | ||
744 | /* | |
745 | * At this point, the line_head is a linked list with each | |
746 | * link node containing another line of the DNS response. | |
747 | * Start parsing... | |
748 | */ | |
749 | if (strstr(line_head->line, "$alive")) { | |
750 | data->answer = cached_curtime; | |
751 | free_lines(line_head); | |
752 | debug(10, "ipcache_parsebuffer: $alive succeeded.\n"); | |
753 | } else if (strstr(line_head->line, "$fail")) { | |
754 | /* | |
755 | * The $fail messages look like: | |
756 | * $fail host\n$message msg\n$end\n | |
757 | */ | |
758 | token = strtok(line_head->line, w_space); /* skip first token */ | |
759 | token = strtok(NULL, w_space); | |
760 | ||
761 | line_cur = line_head->next; | |
762 | if (line_cur && !strncmp(line_cur->line, "$message", 8)) { | |
763 | strcpy(dns_error_message, line_cur->line + 8); | |
764 | } | |
765 | if (token == NULL) { | |
766 | debug(1, "ipcache_parsebuffer: Invalid $fail for DNS table?\n"); | |
767 | } else { | |
768 | plist = globalpending_search(token, data->global_pending); | |
769 | if (plist) { | |
770 | plist->entry->lastref = plist->entry->timestamp = cached_curtime; | |
771 | plist->entry->ttl = IP_NEG_TTL; | |
772 | plist->entry->status = NEGATIVE_CACHED; | |
773 | ipcache_call_pending(plist->entry); | |
774 | globalpending_remove(plist, data); | |
775 | debug(10, "ipcache_parsebuffer: $fail succeeded: %s.\n", | |
776 | dns_error_message[0] ? dns_error_message : "why?"); | |
777 | } else { | |
778 | debug(1, "ipcache_parsebuffer: No entry in DNS table?\n"); | |
779 | } | |
780 | } | |
781 | free_lines(line_head); | |
782 | } else if (strstr(line_head->line, "$name")) { | |
783 | tmp_ptr = line_head->line; | |
784 | /* skip the first token */ | |
785 | token = strtok(tmp_ptr, w_space); | |
786 | tmp_ptr = NULL; | |
787 | token = strtok(tmp_ptr, w_space); | |
788 | if (!token) { | |
789 | debug(1, "ipcache_parsebuffer: Invalid OPCODE for DNS table?\n"); | |
790 | } else { | |
791 | plist = globalpending_search(token, data->global_pending); | |
792 | if (plist) { | |
793 | int ipcount, aliascount; | |
794 | ipcache_entry *e = plist->entry; | |
795 | ||
796 | if (e->status != PENDING) { | |
797 | debug(4, "ipcache_parsebuffer: DNS record already resolved.\n"); | |
798 | } else { | |
799 | e->lastref = e->timestamp = cached_curtime; | |
800 | e->ttl = IP_POS_TTL; | |
801 | e->status = CACHED; | |
802 | ||
803 | line_cur = line_head->next; | |
804 | ||
805 | /* get $h_name */ | |
806 | if (line_cur == NULL || | |
807 | !strstr(line_cur->line, "$h_name")) { | |
808 | debug(1, "ipcache_parsebuffer: DNS record in invalid format? No $h_name.\n"); | |
809 | /* abandon this record */ | |
810 | break; | |
811 | } | |
812 | tmp_ptr = line_cur->line; | |
813 | /* skip the first token */ | |
814 | token = strtok(tmp_ptr, w_space); | |
815 | tmp_ptr = NULL; | |
816 | token = strtok(tmp_ptr, w_space); | |
817 | e->entry.h_name = xstrdup(token); | |
818 | ||
819 | line_cur = line_cur->next; | |
820 | ||
821 | /* get $h_length */ | |
822 | if (line_cur == NULL || | |
823 | !strstr(line_cur->line, "$h_len")) { | |
824 | debug(1, "ipcache_parsebuffer: DNS record in invalid format? No $h_len.\n"); | |
825 | /* abandon this record */ | |
826 | break; | |
827 | } | |
828 | tmp_ptr = line_cur->line; | |
829 | /* skip the first token */ | |
830 | token = strtok(tmp_ptr, w_space); | |
831 | tmp_ptr = NULL; | |
832 | token = strtok(tmp_ptr, w_space); | |
833 | e->entry.h_length = atoi(token); | |
834 | ||
835 | line_cur = line_cur->next; | |
836 | ||
837 | /* get $ipcount */ | |
838 | if (line_cur == NULL || | |
839 | !strstr(line_cur->line, "$ipcount")) { | |
840 | debug(1, "ipcache_parsebuffer: DNS record in invalid format? No $ipcount.\n"); | |
841 | /* abandon this record */ | |
842 | break; | |
843 | } | |
844 | tmp_ptr = line_cur->line; | |
845 | /* skip the first token */ | |
846 | token = strtok(tmp_ptr, w_space); | |
847 | tmp_ptr = NULL; | |
848 | token = strtok(tmp_ptr, w_space); | |
849 | e->addr_count = ipcount = atoi(token); | |
850 | ||
851 | if (ipcount == 0) { | |
852 | e->entry.h_addr_list = NULL; | |
853 | } else { | |
854 | e->entry.h_addr_list = (char **) xcalloc(ipcount, sizeof(char *)); | |
855 | } | |
856 | ||
857 | /* get ip addresses */ | |
858 | { | |
859 | int i = 0; | |
860 | line_cur = line_cur->next; | |
861 | while (i < ipcount) { | |
862 | if (line_cur == NULL) { | |
863 | debug(1, "ipcache_parsebuffer: DNS record in invalid format? No $ipcount data.\n"); | |
864 | break; | |
865 | } | |
866 | e->entry.h_addr_list[i] = (char *) xcalloc(1, e->entry.h_length); | |
867 | *((unsigned long *) e->entry.h_addr_list[i]) = inet_addr(line_cur->line); | |
868 | line_cur = line_cur->next; | |
869 | i++; | |
870 | } | |
871 | } | |
872 | ||
873 | /* get $aliascount */ | |
874 | if (line_cur == NULL || | |
875 | !strstr(line_cur->line, "$aliascount")) { | |
876 | debug(1, "ipcache_parsebuffer: DNS record in invalid format? No $aliascount.\n"); | |
877 | /* abandon this record */ | |
878 | break; | |
879 | } | |
880 | tmp_ptr = line_cur->line; | |
881 | /* skip the first token */ | |
882 | token = strtok(tmp_ptr, w_space); | |
883 | tmp_ptr = NULL; | |
884 | token = strtok(tmp_ptr, w_space); | |
885 | e->alias_count = aliascount = atoi(token); | |
886 | ||
887 | if (aliascount == 0) { | |
888 | e->entry.h_aliases = NULL; | |
889 | } else { | |
890 | e->entry.h_aliases = (char **) xcalloc(aliascount, sizeof(char *)); | |
891 | } | |
892 | ||
893 | /* get aliases */ | |
894 | { | |
895 | int i = 0; | |
896 | line_cur = line_cur->next; | |
897 | while (i < aliascount) { | |
898 | if (line_cur == NULL) { | |
899 | debug(1, "ipcache_parsebuffer: DNS record in invalid format? No $aliascount data.\n"); | |
900 | break; | |
901 | } | |
902 | e->entry.h_aliases[i] = xstrdup(line_cur->line); | |
903 | line_cur = line_cur->next; | |
904 | i++; | |
905 | } | |
906 | } | |
907 | ||
908 | ipcache_call_pending(e); | |
909 | globalpending_remove(plist, data); | |
910 | debug(10, "ipcache_parsebuffer: $name succeeded.\n"); | |
911 | } | |
912 | } else { | |
913 | debug(1, "ipcache_parsebuffer: No entries in DNS $name record?\n"); | |
914 | } | |
915 | } | |
916 | free_lines(line_head); | |
917 | } else { | |
918 | free_lines(line_head); | |
919 | debug(1, "ipcache_parsebuffer: Invalid OPCODE for DNS table?\n"); | |
920 | return -1; | |
921 | } | |
922 | } | |
923 | return (int) (pos - buf); | |
924 | } | |
925 | ||
926 | ||
927 | int ipcache_dnsHandleRead(fd, data) | |
928 | int fd; | |
929 | dnsserver_entry *data; | |
930 | { | |
931 | int char_scanned; | |
932 | int len = read(fd, data->ip_inbuf + data->offset, data->size - data->offset); | |
933 | ||
934 | debug(5, "ipcache_dnsHandleRead: Result from DNS ID %d.\n", data->id); | |
935 | ||
936 | if (len == 0) { | |
937 | debug(1, "ipcache_dnsHandleRead: Connection from DNSSERVER is closed.\n"); | |
938 | debug(1, " Disabling this server ID %d.\n", data->id); | |
939 | data->alive = 0; | |
940 | update_dns_child_alive(); | |
941 | ipcache_cleanup_pendinglist(data); | |
942 | return 0; | |
943 | } | |
944 | data->offset += len; | |
945 | data->ip_inbuf[data->offset] = '\0'; | |
946 | ||
947 | if (strstr(data->ip_inbuf, "$end\n")) { | |
948 | /* end of record found */ | |
949 | char_scanned = ipcache_parsebuffer(data->ip_inbuf, data->offset, data); | |
950 | if (char_scanned > 0) { | |
951 | /* update buffer */ | |
952 | memcpy(data->ip_inbuf, data->ip_inbuf + char_scanned, data->offset - char_scanned); | |
953 | data->offset -= char_scanned; | |
954 | data->ip_inbuf[data->offset] = '\0'; | |
955 | } | |
956 | } | |
957 | /* reschedule */ | |
958 | comm_set_select_handler(data->inpipe, COMM_SELECT_READ, | |
959 | (PF) ipcache_dnsHandleRead, (caddr_t) data); | |
960 | return 0; | |
961 | } | |
962 | ||
963 | int ipcache_nbgethostbyname(name, fd, handler, data) | |
964 | char *name; | |
965 | int fd; | |
966 | IPH handler; | |
967 | caddr_t data; | |
968 | { | |
969 | ipcache_entry *e; | |
970 | IpPending *pending; | |
971 | dnsserver_entry *dns; | |
972 | ||
973 | debug(4, "ipcache_nbgethostbyname: FD %d: Name '%s'.\n", fd, name); | |
974 | ||
975 | if (name == NULL || name[0] == '\0') { | |
976 | debug(4, "ipcache_nbgethostbyname: Invalid name!\n"); | |
977 | ipcache_call_pending_badname(fd, handler, data); | |
978 | return 0; | |
979 | } | |
980 | if ((e = ipcache_get(name)) != NULL && (e->status != PENDING)) { | |
981 | /* hit here */ | |
982 | debug(4, "ipcache_nbgethostbyname: Hit for name '%s'.\n", name); | |
983 | pending = (IpPending *) xcalloc(1, sizeof(IpPending)); | |
984 | pending->fd = fd; | |
985 | pending->handler = handler; | |
986 | pending->data = data; | |
987 | pending->next = NULL; | |
988 | if (e->pending_head == NULL) { /* empty list */ | |
989 | e->pending_head = e->pending_tail = pending; | |
990 | } else { /* add to tail of list */ | |
991 | e->pending_tail->next = pending; | |
992 | e->pending_tail = e->pending_tail->next; | |
993 | } | |
994 | ipcache_call_pending(e); | |
995 | return 0; | |
996 | } | |
997 | debug(4, "ipcache_nbgethostbyname: Name '%s': MISS or PENDING.\n", name); | |
998 | ||
999 | pending = (IpPending *) xcalloc(1, sizeof(IpPending)); | |
1000 | pending->fd = fd; | |
1001 | pending->handler = handler; | |
1002 | pending->data = data; | |
1003 | pending->next = NULL; | |
1004 | if (e == NULL) { | |
1005 | /* No entry, create the new one */ | |
1006 | debug(5, "ipcache_nbgethostbyname: Creating new entry for '%s'...\n", | |
1007 | name); | |
1008 | e = ipcache_create(); | |
1009 | e->name = xstrdup(name); | |
1010 | e->status = PENDING; | |
1011 | e->pending_tail = e->pending_head = pending; | |
1012 | ipcache_add_to_hash(e); | |
1013 | } else { | |
1014 | /* There is an entry. Add handler to list */ | |
1015 | debug(5, "ipcache_nbgethostbyname: Adding handler to pending list for '%s'.\n", name); | |
1016 | if (e->pending_head == NULL) { /* empty list */ | |
1017 | e->pending_head = e->pending_tail = pending; | |
1018 | } else { /* add to tail of list */ | |
1019 | e->pending_tail->next = pending; | |
1020 | e->pending_tail = e->pending_tail->next; | |
1021 | } | |
1022 | return 0; | |
1023 | } | |
1024 | ||
1025 | if (dns_child_alive) { | |
1026 | int i, j, min_dns = 0, min_count = 255, alive = 0; | |
1027 | ||
1028 | j = last_dns_dispatched; | |
1029 | /* select DNS server with the lowest number of pending */ | |
1030 | for (i = 0; i < getDnsChildren(); ++i) { | |
1031 | j += 1; | |
1032 | j %= getDnsChildren(); | |
1033 | if ((dns_child_table[j]->alive) && | |
1034 | (dns_child_table[j]->pending_count < min_count)) { | |
1035 | min_dns = j; | |
1036 | min_count = dns_child_table[j]->pending_count; | |
1037 | } | |
1038 | alive = dns_child_table[j]->alive | alive; | |
1039 | } | |
1040 | ||
1041 | if (alive == 0) { | |
1042 | dns_child_alive = 0; /* all dead */ | |
1043 | last_dns_dispatched = 0; /* use entry 0 */ | |
1044 | } else { | |
1045 | last_dns_dispatched = min_dns; | |
1046 | } | |
1047 | } else { | |
1048 | last_dns_dispatched = 0; | |
1049 | } | |
1050 | ||
1051 | dns = dns_child_table[last_dns_dispatched]; | |
1052 | debug(5, "ipcache_nbgethostbyname: Dispatched DNS %d.\n", | |
1053 | last_dns_dispatched); | |
1054 | ||
1055 | /* add to global pending list */ | |
1056 | if (dns->global_pending == NULL) { /* new list */ | |
1057 | dns->global_pending = (ipcache_list *) xcalloc(1, sizeof(ipcache_list)); | |
1058 | dns->global_pending->entry = e; | |
1059 | dns->global_pending->next = NULL; | |
1060 | dns->global_pending_tail = dns->global_pending; | |
1061 | } else { /* add to end of list */ | |
1062 | ipcache_list *p = (ipcache_list *) xcalloc(1, sizeof(ipcache_list)); | |
1063 | p->entry = e; | |
1064 | p->next = NULL; | |
1065 | dns->global_pending_tail->next = p; | |
1066 | dns->global_pending_tail = dns->global_pending_tail->next; | |
1067 | } | |
1068 | ||
1069 | if (dns_child_alive) { | |
1070 | char *buf = (char *) xcalloc(1, 256); | |
1071 | strncpy(buf, name, 254); | |
1072 | strcat(buf, "\n"); | |
1073 | dns->pending_count++; | |
1074 | file_write(dns->outpipe, | |
1075 | buf, | |
1076 | strlen(buf), | |
1077 | 0, /* Lock */ | |
1078 | 0, /* Handler */ | |
1079 | 0); /* Handler-data */ | |
1080 | ||
1081 | debug(5, "ipcache_nbgethostbyname: Request sent DNS server ID %d.\n", last_dns_dispatched); | |
1082 | } else { | |
1083 | /* do a blocking mode */ | |
1084 | debug(4, "ipcache_nbgethostbyname: Fall back to blocking mode. Server's dead...\n"); | |
1085 | ipcache_cleanup_pendinglist(dns); | |
1086 | } | |
1087 | return 0; | |
1088 | } | |
1089 | ||
1090 | ||
1091 | /* initialize the ipcache */ | |
1092 | void ipcache_init() | |
1093 | { | |
1094 | int i, dnssocket; | |
1095 | char fd_note_buf[FD_ASCII_NOTE_SZ]; | |
1096 | ||
1097 | debug(3, "ipcache_init: Called. ipcache_initialized=%d getDnsChildren()=%d\n", ipcache_initialized, getDnsChildren()); | |
1098 | ||
1099 | if (ipcache_initialized) | |
1100 | return; | |
1101 | ||
090089c4 | 1102 | if (mkdir("dns", 0755) < 0 && errno != EEXIST) { |
1103 | debug(0, "ipcache_init: mkdir %s\n", xstrerror()); | |
1104 | } | |
090089c4 | 1105 | last_dns_dispatched = getDnsChildren() - 1; |
1106 | dns_error_message = xcalloc(1, 256); | |
1107 | ||
1108 | /* test naming lookup */ | |
1109 | if (!do_dns_test) { | |
1110 | debug(4, "ipcache_init: Skipping DNS name lookup tests, -D flag given.\n"); | |
1111 | } else if (ipcache_testname() < 0) { | |
1112 | debug(0, "ipcache_init: DNS name lookup appears to be broken on this machine.\n"); | |
1113 | fprintf(stderr, "ipcache_init: DNS name lookup appears to be broken on this machine.\n"); | |
1114 | exit(-1); | |
1115 | } else { | |
1116 | debug(4, "ipcache_init: Successful DNS name lookup tests...\n"); | |
1117 | } | |
1118 | ||
1119 | ip_table = hash_create(urlcmp, 229); /* small hash table */ | |
1120 | /* init static area */ | |
1121 | static_result = (struct hostent *) xcalloc(1, sizeof(struct hostent)); | |
1122 | static_result->h_length = 4; | |
1123 | /* Need a terminating NULL address (h_addr_list[1]) */ | |
1124 | static_result->h_addr_list = (char **) xcalloc(2, sizeof(char *)); | |
1125 | static_result->h_addr_list[0] = (char *) xcalloc(1, 4); | |
1126 | static_result->h_name = (char *) xcalloc(1, MAX_HOST_NAME + 1); | |
1127 | /* start up companion process */ | |
1128 | dns_child_table = (dnsserver_entry **) xcalloc(getDnsChildren(), sizeof(dnsserver_entry)); | |
1129 | dns_child_alive = 0; | |
1130 | debug(1, "ipcache_init: Starting %d 'dns_server' processes\n", | |
1131 | getDnsChildren()); | |
56b69519 | 1132 | for (i = 0; i < getDnsChildren(); i++) { |
090089c4 | 1133 | dns_child_table[i] = (dnsserver_entry *) xcalloc(1, sizeof(dnsserver_entry)); |
1134 | if ((dnssocket = ipcache_create_dnsserver(getDnsProgram())) < 0) { | |
1135 | debug(1, "ipcache_init: WARNING: Cannot run 'dnsserver' process.\n"); | |
1136 | debug(1, " Fallling back to the blocking version.\n"); | |
1137 | dns_child_table[i]->alive = 0; | |
1138 | } else { | |
1139 | dns_child_alive = 1; | |
1140 | dns_child_table[i]->id = i; | |
1141 | dns_child_table[i]->inpipe = dnssocket; | |
1142 | dns_child_table[i]->outpipe = dnssocket; | |
1143 | dns_child_table[i]->lastcall = cached_curtime; | |
1144 | dns_child_table[i]->pending_count = 0; | |
1145 | dns_child_table[i]->size = IP_INBUF - 1; /* spare one for \0 */ | |
1146 | dns_child_table[i]->offset = 0; | |
1147 | dns_child_table[i]->alive = 1; | |
1148 | dns_child_table[i]->ip_inbuf = (char *) xcalloc(1, IP_INBUF); | |
1149 | ||
1150 | /* update fd_stat */ | |
1151 | ||
090089c4 | 1152 | sprintf(fd_note_buf, "%s #%d", |
1153 | getDnsProgram(), | |
1154 | dns_child_table[i]->id); | |
56b69519 | 1155 | file_update_open(dns_child_table[i]->inpipe, fd_note_buf); |
1156 | ||
1157 | debug(5, "Calling fd_note() with FD %d and buf '%s'\n", | |
1158 | dns_child_table[i]->inpipe, fd_note_buf); | |
090089c4 | 1159 | |
1160 | fd_note(dns_child_table[i]->inpipe, fd_note_buf); | |
1161 | commSetNonBlocking(dns_child_table[i]->inpipe); | |
1162 | ||
1163 | /* clear unused handlers */ | |
56b69519 | 1164 | comm_set_select_handler(dns_child_table[i]->inpipe, |
1165 | COMM_SELECT_WRITE, | |
1166 | 0, | |
1167 | 0); | |
1168 | comm_set_select_handler(dns_child_table[i]->outpipe, | |
1169 | COMM_SELECT_READ, | |
1170 | 0, | |
1171 | 0); | |
090089c4 | 1172 | |
1173 | /* set handler for incoming result */ | |
56b69519 | 1174 | comm_set_select_handler(dns_child_table[i]->inpipe, |
1175 | COMM_SELECT_READ, | |
1176 | (PF) ipcache_dnsHandleRead, | |
1177 | (caddr_t) dns_child_table[i]); | |
090089c4 | 1178 | debug(3, "ipcache_init: 'dns_server' %d started\n", i); |
1179 | } | |
1180 | } | |
1181 | ipcache_high = (long) (((float) MAX_IP * | |
1182 | (float) IP_HIGH_WATER) / (float) 100); | |
1183 | ipcache_low = (long) (((float) MAX_IP * | |
1184 | (float) IP_LOW_WATER) / (float) 100); | |
1185 | ||
1186 | ||
1187 | ipcache_initialized = 1; | |
1188 | } | |
1189 | ||
1190 | /* clean up the pending entries in dnsserver */ | |
1191 | /* return 1 if we found the host, 0 otherwise */ | |
1192 | int ipcache_unregister(name, fd) | |
1193 | char *name; | |
1194 | int fd; | |
1195 | { | |
1196 | ipcache_entry *e; | |
1197 | IpPending *p, *q; | |
1198 | ||
1199 | e = ipcache_get(name); | |
1200 | if (!e) { | |
1201 | /* not found any where */ | |
1202 | return 0; | |
1203 | } | |
1204 | /* look for matched fd */ | |
1205 | for (q = p = e->pending_head; p; q = p, p = p->next) { | |
1206 | if (p->fd == fd) { | |
1207 | break; | |
1208 | } | |
1209 | } | |
1210 | ||
1211 | if (p == NULL) { | |
1212 | /* Can not find this ipcache_entry, weird */ | |
1213 | debug(1, "ipcache_unregister: Failed to unregister FD %d from name: %s, can't find this FD.\n", | |
1214 | fd, name); | |
1215 | return 0; | |
1216 | } | |
1217 | /* found */ | |
1218 | if (p == e->pending_head) { | |
1219 | /* it's at the head of the queue */ | |
1220 | if (p->next) { | |
1221 | /* there is something along the line */ | |
1222 | e->pending_head = p->next; | |
1223 | free(p->data); | |
1224 | free(p); | |
1225 | } else { | |
1226 | /* it is the only entry */ | |
1227 | e->pending_head = e->pending_tail = NULL; | |
1228 | free(p->data); | |
1229 | free(p); | |
1230 | } | |
1231 | } else if (p == e->pending_tail) { | |
1232 | /* it's at the tail */ | |
1233 | e->pending_tail = q; | |
1234 | q->next = NULL; | |
1235 | free(p->data); | |
1236 | free(p); | |
1237 | } else { | |
1238 | /* it's in the middle */ | |
1239 | /* skip it in the list */ | |
1240 | q->next = p->next; | |
1241 | free(p->data); | |
1242 | free(p); | |
1243 | } | |
1244 | return 1; | |
1245 | } | |
1246 | ||
090089c4 | 1247 | |
1248 | struct hostent *ipcache_gethostbyname(name) | |
1249 | char *name; | |
1250 | { | |
1251 | ipcache_entry *result; | |
1252 | unsigned int a1, a2, a3, a4; | |
1253 | struct hostent *s_result = NULL; | |
1254 | ||
1255 | if (!ipcache_initialized) | |
1256 | ipcache_init(); | |
1257 | ||
1258 | if (!name) { | |
1259 | debug(5, "ipcache_gethostbyname: Invalid argument?\n"); | |
1260 | return (NULL); | |
1261 | } | |
1262 | if (!(result = ipcache_get(name))) { | |
1263 | /* cache miss */ | |
1264 | if (name) { | |
1265 | debug(5, "ipcache_gethostbyname: IPcache miss for '%s'.\n", name); | |
1266 | } | |
1267 | /* check if it's already a IP address in text form. */ | |
1268 | if (sscanf(name, "%u.%u.%u.%u", &a1, &a2, &a3, &a4) == 4) { | |
1269 | *((unsigned long *) static_result->h_addr_list[0]) = inet_addr(name); | |
1270 | strncpy(static_result->h_name, name, MAX_HOST_NAME); | |
1271 | return static_result; | |
1272 | } else { | |
1273 | s_result = gethostbyname(name); | |
1274 | } | |
1275 | ||
1276 | if (s_result && s_result->h_name && (s_result->h_name[0] != '\0')) { | |
1277 | /* good address, cached */ | |
1278 | debug(10, "ipcache_gethostbyname: DNS success: cache for '%s'.\n", name); | |
1279 | ipcache_add(name, ipcache_create(), s_result, 1); | |
1280 | result = ipcache_get(name); | |
1281 | return &(result->entry); | |
1282 | } else { | |
1283 | /* bad address, negative cached */ | |
1284 | debug(3, "ipcache_gethostbyname: DNS failure: negative cache for '%s'.\n", name); | |
1285 | ipcache_add(name, ipcache_create(), s_result, 0); | |
1286 | return NULL; | |
1287 | } | |
1288 | ||
1289 | } | |
1290 | /* cache hit */ | |
1291 | debug(5, "ipcache_gethostbyname: Hit for '%s'.\n", name ? name : "NULL"); | |
1292 | result->lastref = cached_curtime; | |
1293 | return (result->status == CACHED) ? &(result->entry) : NULL; | |
1294 | } | |
1295 | ||
1296 | ||
090089c4 | 1297 | |
1298 | /* process objects list */ | |
1299 | void stat_ipcache_get(sentry, obj) | |
1300 | StoreEntry *sentry; | |
1301 | cacheinfo *obj; | |
1302 | { | |
1303 | char buffer[MAX_LINELEN]; | |
1304 | ipcache_entry *e = NULL; | |
1305 | int i; | |
1306 | int ttl; | |
1307 | char status; | |
1308 | ||
1309 | sprintf(buffer, "{IP Cache Contents:\n\n"); | |
1310 | storeAppend(sentry, buffer, strlen(buffer)); | |
1311 | ||
1312 | for (e = ipcache_GetFirst(); (e); e = ipcache_GetNext()) { | |
1313 | if (e) { | |
1314 | ttl = (e->ttl - cached_curtime + e->lastref); | |
1315 | status = ipcache_status_char(e); | |
1316 | if (status == 'P') | |
1317 | ttl = 0; | |
1318 | ||
1319 | sprintf(buffer, " {%s %c %d %d", | |
1320 | e->name, status, ttl, e->addr_count); | |
1321 | storeAppend(sentry, buffer, strlen(buffer)); | |
1322 | ||
1323 | for (i = 0; i < (int) e->addr_count; i++) { | |
1324 | struct in_addr addr; | |
1325 | memcpy((char *) &addr, e->entry.h_addr_list[i], e->entry.h_length); | |
1326 | ||
1327 | sprintf(buffer, "%s ", inet_ntoa(addr)); | |
1328 | storeAppend(sentry, buffer, strlen(buffer)); | |
1329 | } | |
1330 | for (i = 0; i < (int) e->alias_count; i++) { | |
1331 | sprintf(buffer, "%s ", e->entry.h_aliases[i]); | |
1332 | storeAppend(sentry, buffer, strlen(buffer)); | |
1333 | } | |
1334 | if (e->entry.h_name && strncmp(e->name, e->entry.h_name, MAX_LINELEN)) { | |
1335 | sprintf(buffer, "%s ", e->entry.h_name); | |
1336 | storeAppend(sentry, buffer, strlen(buffer)); | |
1337 | } | |
1338 | sprintf(buffer, "}\n"); | |
1339 | storeAppend(sentry, buffer, strlen(buffer)); | |
1340 | } | |
1341 | } | |
1342 | sprintf(buffer, "}\n"); | |
1343 | storeAppend(sentry, buffer, strlen(buffer)); | |
1344 | ||
1345 | } | |
1346 | ||
1347 | char ipcache_status_char(e) | |
1348 | ipcache_entry *e; | |
1349 | { | |
1350 | switch (e->status) { | |
1351 | case CACHED: | |
1352 | return ('C'); | |
1353 | case PENDING: | |
1354 | return ('P'); | |
1355 | case NEGATIVE_CACHED: | |
1356 | return ('N'); | |
1357 | default: | |
1358 | debug(1, "ipcache_status_char: unexpected IP cache status.\n"); | |
1359 | } | |
1360 | return ('X'); | |
1361 | } | |
1362 | ||
1363 | int ipcache_hash_entry_count() | |
1364 | { | |
1365 | ipcache_entry *e; | |
1366 | int local_ip_count = 0; | |
1367 | ||
1368 | e = NULL; | |
1369 | ||
1370 | for (e = ipcache_GetFirst(); e; e = ipcache_GetNext()) { | |
1371 | local_ip_count++; | |
1372 | } | |
1373 | ||
1374 | return local_ip_count; | |
1375 | } |