]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ipcache.cc
Adding blocklist patch
[thirdparty/squid.git] / src / ipcache.cc
CommitLineData
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
33struct hostent *gethostbyname();
34int urlcmp _PARAMS((char *s1, char *s2));
35
36#define MAX_LINELEN (4096)
37char ipcache_status_char _PARAMS((ipcache_entry *));
38int 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
48long ipcache_low = 180;
49long ipcache_high = 200;
50
51typedef struct _ip_pending {
52 int fd;
53 IPH handler;
54 caddr_t data;
55 struct _ip_pending *next;
56} IpPending;
57
58
59typedef struct _ipcache_list {
60 ipcache_entry *entry;
61 struct _ipcache_list *next;
62} ipcache_list;
63
64
65typedef 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
81typedef struct _line_entry {
82 char *line;
83 struct _line_entry *next;
84} line_entry;
85
86#define TEST_SITE 5
87static char *test_site[TEST_SITE] =
88{
89 "internic.net",
90 "usc.edu",
91 "cs.colorado.edu",
92 "mit.edu",
93 "yale.edu"
94};
95
96static char w_space[] = " \t\n";
97static dnsserver_entry **dns_child_table = NULL;
98static int last_dns_dispatched = 2;
99static struct hostent *static_result = NULL;
090089c4 100static int dns_child_alive = 0;
101static int ipcache_initialized = 0;
102
103char *dns_error_message = NULL; /* possible error message */
104HashID ip_table = 0;
105
106extern int do_dns_test;
107extern time_t cached_curtime;
108extern int getMaxFD();
109extern int getDnsChildren();
110extern void fatal_dump _PARAMS((char *));
1ac90c59 111extern int file_update_open _PARAMS((int, char *));
090089c4 112
113void 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
126int 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 */
143int 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 */
215int 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 */
267ipcache_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 */
291ipcache_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 */
302ipcache_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
311int 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 */
329int 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 */
385ipcache_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
416void 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
430void 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 */
496void 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 */
553void 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
578void 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 */
591int 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 */
613void 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 */
627ipcache_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 */
648void 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 */
692int 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
927int 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
963int 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 */
1092void 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 */
1192int 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
1248struct 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 */
1299void 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
1347char 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
1363int 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}