]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ipcache.cc
Import of fix-ranges branch
[thirdparty/squid.git] / src / ipcache.cc
1
2 /*
3 * $Id: ipcache.cc,v 1.240 2003/01/23 00:37:22 robertc Exp $
4 *
5 * DEBUG: section 14 IP Cache
6 * AUTHOR: Harvest Derived
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36 #include "squid.h"
37 #include "Store.h"
38
39 typedef struct _ipcache_entry ipcache_entry;
40
41 struct _ipcache_entry {
42 hash_link hash; /* must be first */
43 time_t lastref;
44 time_t expires;
45 ipcache_addrs addrs;
46 IPH *handler;
47 void *handlerData;
48 char *error_message;
49 struct timeval request_time;
50 dlink_node lru;
51 unsigned short locks;
52 struct {
53 unsigned int negcached:1;
54 unsigned int fromhosts:1;
55 } flags;
56 };
57
58 static struct {
59 int requests;
60 int replies;
61 int hits;
62 int misses;
63 int negative_hits;
64 int errors;
65 int ghbn_calls; /* # calls to blocking gethostbyname() */
66 int release_locked;
67 } IpcacheStats;
68
69 static dlink_list lru_list;
70
71 static FREE ipcacheFreeEntry;
72 #if USE_DNSSERVERS
73 static HLPCB ipcacheHandleReply;
74 #else
75 static IDNSCB ipcacheHandleReply;
76 #endif
77 static IPH dummy_handler;
78 static int ipcacheExpiredEntry(ipcache_entry *);
79 static int ipcache_testname(void);
80 #if USE_DNSSERVERS
81 static ipcache_entry *ipcacheParse(const char *buf);
82 #else
83 static ipcache_entry *ipcacheParse(rfc1035_rr *, int);
84 #endif
85 static ipcache_entry *ipcache_get(const char *);
86 static void ipcacheLockEntry(ipcache_entry *);
87 static void ipcacheStatPrint(ipcache_entry *, StoreEntry *);
88 static void ipcacheUnlockEntry(ipcache_entry *);
89 static void ipcacheRelease(ipcache_entry *);
90
91 static ipcache_addrs static_addrs;
92 static hash_table *ip_table = NULL;
93
94 static long ipcache_low = 180;
95 static long ipcache_high = 200;
96
97 #if LIBRESOLV_DNS_TTL_HACK
98 extern int _dns_ttl_;
99 #endif
100
101 static int
102 ipcache_testname(void)
103 {
104 wordlist *w = NULL;
105 debug(14, 1) ("Performing DNS Tests...\n");
106 if ((w = Config.dns_testname_list) == NULL)
107 return 1;
108 for (; w; w = w->next) {
109 IpcacheStats.ghbn_calls++;
110 if (gethostbyname(w->key) != NULL)
111 return 1;
112 }
113 return 0;
114 }
115
116 /* removes the given ipcache entry */
117 static void
118 ipcacheRelease(ipcache_entry * i)
119 {
120 hash_remove_link(ip_table, (hash_link *) i);
121 dlinkDelete(&i->lru, &lru_list);
122 ipcacheFreeEntry(i);
123 }
124
125 static ipcache_entry *
126 ipcache_get(const char *name)
127 {
128 if (ip_table != NULL)
129 return (ipcache_entry *) hash_lookup(ip_table, name);
130 else
131 return NULL;
132 }
133
134 static int
135 ipcacheExpiredEntry(ipcache_entry * i)
136 {
137 /* all static entries are locked, so this takes care of them too */
138 if (i->locks != 0)
139 return 0;
140 if (i->addrs.count == 0)
141 if (0 == i->flags.negcached)
142 return 1;
143 if (i->expires > squid_curtime)
144 return 0;
145 return 1;
146 }
147
148 void
149 ipcache_purgelru(void *voidnotused)
150 {
151 dlink_node *m;
152 dlink_node *prev = NULL;
153 ipcache_entry *i;
154 int removed = 0;
155 eventAdd("ipcache_purgelru", ipcache_purgelru, NULL, 10.0, 1);
156 for (m = lru_list.tail; m; m = prev) {
157 if (memInUse(MEM_IPCACHE_ENTRY) < ipcache_low)
158 break;
159 prev = m->prev;
160 i = (ipcache_entry *)m->data;
161 if (i->locks != 0)
162 continue;
163 ipcacheRelease(i);
164 removed++;
165 }
166 debug(14, 9) ("ipcache_purgelru: removed %d entries\n", removed);
167 }
168
169 /* purges entries added from /etc/hosts (or whatever). */
170 static void
171 purge_entries_fromhosts(void)
172 {
173 dlink_node *m = lru_list.head;
174 ipcache_entry *i = NULL, *t;
175 while (m) {
176 if (i != NULL) { /* need to delay deletion */
177 ipcacheRelease(i); /* we just override locks */
178 i = NULL;
179 }
180 t = (ipcache_entry*)m->data;
181 if (t->flags.fromhosts)
182 i = t;
183 m = m->next;
184 }
185 if (i != NULL)
186 ipcacheRelease(i);
187 }
188
189 /* create blank ipcache_entry */
190 static ipcache_entry *
191 ipcacheCreateEntry(const char *name)
192 {
193 static ipcache_entry *i;
194 i = (ipcache_entry *)memAllocate(MEM_IPCACHE_ENTRY);
195 i->hash.key = xstrdup(name);
196 i->expires = squid_curtime + Config.negativeDnsTtl;
197 return i;
198 }
199
200 static void
201 ipcacheAddEntry(ipcache_entry * i)
202 {
203 hash_link *e = (hash_link *)hash_lookup(ip_table, i->hash.key);
204 if (NULL != e) {
205 /* avoid colission */
206 ipcache_entry *q = (ipcache_entry *) e;
207 ipcacheRelease(q);
208 }
209 hash_join(ip_table, &i->hash);
210 dlinkAdd(i, &i->lru, &lru_list);
211 i->lastref = squid_curtime;
212 }
213
214 /* walks down the pending list, calling handlers */
215 static void
216 ipcacheCallback(ipcache_entry * i)
217 {
218 IPH *callback = i->handler;
219 void *cbdata;
220 i->lastref = squid_curtime;
221 if (!i->handler)
222 return;
223 ipcacheLockEntry(i);
224 callback = i->handler;
225 i->handler = NULL;
226 if (cbdataReferenceValidDone(i->handlerData, &cbdata)) {
227 dns_error_message = i->error_message;
228 callback(i->flags.negcached ? NULL : &i->addrs, cbdata);
229 }
230 ipcacheUnlockEntry(i);
231 }
232
233 static ipcache_entry *
234 #if USE_DNSSERVERS
235 ipcacheParse(const char *inbuf)
236 {
237 LOCAL_ARRAY(char, buf, DNS_INBUF_SZ);
238 char *token;
239 static ipcache_entry i;
240 int j;
241 int k;
242 int ipcount = 0;
243 int ttl;
244 char A[32][16];
245 memset(&i, '\0', sizeof(i));
246 i.expires = squid_curtime;
247 i.flags.negcached = 1;
248 if (inbuf == NULL) {
249 debug(14, 1) ("ipcacheParse: Got <NULL> reply\n");
250 i.error_message = xstrdup("Internal Squid Error");
251 return &i;
252 }
253 xstrncpy(buf, inbuf, DNS_INBUF_SZ);
254 debug(14, 5) ("ipcacheParse: parsing: {%s}\n", buf);
255 token = strtok(buf, w_space);
256 if (NULL == token) {
257 debug(14, 1) ("ipcacheParse: Got <NULL>, expecting '$addr'\n");
258 return &i;
259 }
260 if (0 == strcmp(token, "$fail")) {
261 i.expires = squid_curtime + Config.negativeDnsTtl;
262 token = strtok(NULL, "\n");
263 assert(NULL != token);
264 i.error_message = xstrdup(token);
265 return &i;
266 }
267 if (0 != strcmp(token, "$addr")) {
268 debug(14, 1) ("ipcacheParse: Got '%s', expecting '$addr'\n", token);
269 return &i;
270 }
271 token = strtok(NULL, w_space);
272 if (NULL == token) {
273 debug(14, 1) ("ipcacheParse: Got <NULL>, expecting TTL\n");
274 return &i;
275 }
276 i.flags.negcached = 0;
277 ttl = atoi(token);
278 if (ttl > 0)
279 i.expires = squid_curtime + ttl;
280 else
281 i.expires = squid_curtime + Config.positiveDnsTtl;
282 while (NULL != (token = strtok(NULL, w_space))) {
283 xstrncpy(A[ipcount], token, 16);
284 if (++ipcount == 32)
285 break;
286 }
287 if (0 == ipcount) {
288 i.addrs.in_addrs = NULL;
289 i.addrs.bad_mask = NULL;
290 } else {
291 i.addrs.in_addrs = (struct in_addr *)xcalloc(ipcount, sizeof(struct in_addr));
292 i.addrs.bad_mask = (unsigned char *)xcalloc(ipcount, sizeof(unsigned char));
293 }
294 for (j = 0, k = 0; k < ipcount; k++) {
295 if (safe_inet_addr(A[k], &i.addrs.in_addrs[j]))
296 j++;
297 else
298 debug(14, 1) ("ipcacheParse: Invalid IP address '%s'\n", A[k]);
299 }
300 i.addrs.count = (unsigned char) j;
301 return &i;
302 }
303 #else
304 ipcacheParse(rfc1035_rr * answers, int nr)
305 {
306 static ipcache_entry i;
307 int k;
308 int j;
309 int na = 0;
310 memset(&i, '\0', sizeof(i));
311 i.expires = squid_curtime + Config.negativeDnsTtl;
312 i.flags.negcached = 1;
313 if (nr < 0) {
314 debug(14, 3) ("ipcacheParse: Lookup failed (error %d)\n",
315 rfc1035_errno);
316 assert(rfc1035_error_message);
317 i.error_message = xstrdup(rfc1035_error_message);
318 return &i;
319 }
320 if (nr == 0) {
321 debug(14, 3) ("ipcacheParse: No DNS records\n");
322 i.error_message = xstrdup("No DNS records");
323 return &i;
324 }
325 assert(answers);
326 for (j = 0, k = 0; k < nr; k++) {
327 if (answers[k].type != RFC1035_TYPE_A)
328 continue;
329 if (answers[k]._class != RFC1035_CLASS_IN)
330 continue;
331 na++;
332 }
333 if (na == 0) {
334 debug(14, 1) ("ipcacheParse: No Address records\n");
335 i.error_message = xstrdup("No Address records");
336 return &i;
337 }
338 i.flags.negcached = 0;
339 i.addrs.in_addrs = (struct in_addr *)xcalloc(na, sizeof(struct in_addr));
340 i.addrs.bad_mask = (unsigned char *)xcalloc(na, sizeof(unsigned char));
341 i.addrs.count = (unsigned char) na;
342 for (j = 0, k = 0; k < nr; k++) {
343 if (answers[k].type != RFC1035_TYPE_A)
344 continue;
345 if (answers[k]._class != RFC1035_CLASS_IN)
346 continue;
347 if (j == 0)
348 i.expires = squid_curtime + answers[k].ttl;
349 assert(answers[k].rdlength == 4);
350 xmemcpy(&i.addrs.in_addrs[j++], answers[k].rdata, 4);
351 debug(14, 3) ("ipcacheParse: #%d %s\n",
352 j - 1,
353 inet_ntoa(i.addrs.in_addrs[j - 1]));
354 }
355 assert(j == na);
356 return &i;
357 }
358 #endif
359
360 static void
361 #if USE_DNSSERVERS
362 ipcacheHandleReply(void *data, char *reply)
363 #else
364 ipcacheHandleReply(void *data, rfc1035_rr * answers, int na)
365 #endif
366 {
367 generic_cbdata *c = (generic_cbdata *)data;
368 ipcache_entry *i = (ipcache_entry *)c->data;
369 ipcache_entry *x = NULL;
370 cbdataFree(c);
371 c = NULL;
372 IpcacheStats.replies++;
373 statHistCount(&statCounter.dns.svc_time,
374 tvSubMsec(i->request_time, current_time));
375 #if USE_DNSSERVERS
376 x = ipcacheParse(reply);
377 #else
378 x = ipcacheParse(answers, na);
379 #endif
380 assert(x);
381 i->addrs = x->addrs;
382 i->error_message = x->error_message;
383 i->expires = x->expires;
384 i->flags = x->flags;
385 ipcacheAddEntry(i);
386 ipcacheCallback(i);
387 }
388
389 void
390 ipcache_nbgethostbyname(const char *name, IPH * handler, void *handlerData)
391 {
392 ipcache_entry *i = NULL;
393 const ipcache_addrs *addrs = NULL;
394 generic_cbdata *c;
395 assert(handler != NULL);
396 debug(14, 4) ("ipcache_nbgethostbyname: Name '%s'.\n", name);
397 IpcacheStats.requests++;
398 if (name == NULL || name[0] == '\0') {
399 debug(14, 4) ("ipcache_nbgethostbyname: Invalid name!\n");
400 handler(NULL, handlerData);
401 return;
402 }
403 if ((addrs = ipcacheCheckNumeric(name))) {
404 handler(addrs, handlerData);
405 return;
406 }
407 i = ipcache_get(name);
408 if (NULL == i) {
409 /* miss */
410 (void) 0;
411 } else if (ipcacheExpiredEntry(i)) {
412 /* hit, but expired -- bummer */
413 ipcacheRelease(i);
414 i = NULL;
415 } else {
416 /* hit */
417 debug(14, 4) ("ipcache_nbgethostbyname: HIT for '%s'\n", name);
418 if (i->flags.negcached)
419 IpcacheStats.negative_hits++;
420 else
421 IpcacheStats.hits++;
422 i->handler = handler;
423 i->handlerData = cbdataReference(handlerData);
424 ipcacheCallback(i);
425 return;
426 }
427 debug(14, 5) ("ipcache_nbgethostbyname: MISS for '%s'\n", name);
428 IpcacheStats.misses++;
429 i = ipcacheCreateEntry(name);
430 i->handler = handler;
431 i->handlerData = cbdataReference(handlerData);
432 i->request_time = current_time;
433 c = cbdataAlloc(generic_cbdata);
434 c->data = i;
435 #if USE_DNSSERVERS
436 dnsSubmit(hashKeyStr(&i->hash), ipcacheHandleReply, c);
437 #else
438 idnsALookup(hashKeyStr(&i->hash), ipcacheHandleReply, c);
439 #endif
440 }
441
442 /* initialize the ipcache */
443 void
444 ipcache_init(void)
445 {
446 int n;
447 debug(14, 3) ("Initializing IP Cache...\n");
448 memset(&IpcacheStats, '\0', sizeof(IpcacheStats));
449 memset(&lru_list, '\0', sizeof(lru_list));
450 /* test naming lookup */
451 if (!opt_dns_tests) {
452 debug(14, 4) ("ipcache_init: Skipping DNS name lookup tests.\n");
453 } else if (!ipcache_testname()) {
454 fatal("ipcache_init: DNS name lookup tests failed.");
455 } else {
456 debug(14, 1) ("Successful DNS name lookup tests...\n");
457 }
458 memset(&static_addrs, '\0', sizeof(ipcache_addrs));
459 static_addrs.in_addrs = (struct in_addr *)xcalloc(1, sizeof(struct in_addr));
460 static_addrs.bad_mask = (unsigned char *)xcalloc(1, sizeof(unsigned char));
461 ipcache_high = (long) (((float) Config.ipcache.size *
462 (float) Config.ipcache.high) / (float) 100);
463 ipcache_low = (long) (((float) Config.ipcache.size *
464 (float) Config.ipcache.low) / (float) 100);
465 n = hashPrime(ipcache_high / 4);
466 ip_table = hash_create((HASHCMP *) strcmp, n, hash4);
467 cachemgrRegister("ipcache",
468 "IP Cache Stats and Contents",
469 stat_ipcache_get, 0, 1);
470 memDataInit(MEM_IPCACHE_ENTRY, "ipcache_entry", sizeof(ipcache_entry), 0);
471 }
472
473 const ipcache_addrs *
474 ipcache_gethostbyname(const char *name, int flags)
475 {
476 ipcache_entry *i = NULL;
477 ipcache_addrs *addrs;
478 assert(name);
479 debug(14, 3) ("ipcache_gethostbyname: '%s', flags=%x\n", name, flags);
480 IpcacheStats.requests++;
481 i = ipcache_get(name);
482 if (NULL == i) {
483 (void) 0;
484 } else if (ipcacheExpiredEntry(i)) {
485 ipcacheRelease(i);
486 i = NULL;
487 } else if (i->flags.negcached) {
488 IpcacheStats.negative_hits++;
489 dns_error_message = i->error_message;
490 return NULL;
491 } else {
492 IpcacheStats.hits++;
493 i->lastref = squid_curtime;
494 return &i->addrs;
495 }
496 if ((addrs = ipcacheCheckNumeric(name)))
497 return addrs;
498 IpcacheStats.misses++;
499 if (flags & IP_LOOKUP_IF_MISS)
500 ipcache_nbgethostbyname(name, dummy_handler, NULL);
501 return NULL;
502 }
503
504 static void
505 ipcacheStatPrint(ipcache_entry * i, StoreEntry * sentry)
506 {
507 int k;
508 storeAppendPrintf(sentry, " %-32.32s %c%c %6d %6d %2d(%2d)",
509 hashKeyStr(&i->hash),
510 i->flags.fromhosts ? 'H' : ' ',
511 i->flags.negcached ? 'N' : ' ',
512 (int) (squid_curtime - i->lastref),
513 (int) ((i->flags.fromhosts ? -1 : i->expires - squid_curtime)),
514 (int) i->addrs.count,
515 (int) i->addrs.badcount);
516 for (k = 0; k < (int) i->addrs.count; k++) {
517 storeAppendPrintf(sentry, " %15s-%3s", inet_ntoa(i->addrs.in_addrs[k]),
518 i->addrs.bad_mask[k] ? "BAD" : "OK ");
519 }
520 storeAppendPrintf(sentry, "\n");
521 }
522
523 /* process objects list */
524 void
525 stat_ipcache_get(StoreEntry * sentry)
526 {
527 dlink_node *m;
528 assert(ip_table != NULL);
529 storeAppendPrintf(sentry, "IP Cache Statistics:\n");
530 storeAppendPrintf(sentry, "IPcache Entries: %d\n",
531 memInUse(MEM_IPCACHE_ENTRY));
532 storeAppendPrintf(sentry, "IPcache Requests: %d\n",
533 IpcacheStats.requests);
534 storeAppendPrintf(sentry, "IPcache Hits: %d\n",
535 IpcacheStats.hits);
536 storeAppendPrintf(sentry, "IPcache Negative Hits: %d\n",
537 IpcacheStats.negative_hits);
538 storeAppendPrintf(sentry, "IPcache Misses: %d\n",
539 IpcacheStats.misses);
540 storeAppendPrintf(sentry, "Blocking calls to gethostbyname(): %d\n",
541 IpcacheStats.ghbn_calls);
542 storeAppendPrintf(sentry, "Attempts to release locked entries: %d\n",
543 IpcacheStats.release_locked);
544 storeAppendPrintf(sentry, "\n\n");
545 storeAppendPrintf(sentry, "IP Cache Contents:\n\n");
546 storeAppendPrintf(sentry, " %-29.29s %3s %6s %6s %1s\n",
547 "Hostname",
548 "Flg",
549 "lstref",
550 "TTL",
551 "N");
552 for (m = lru_list.head; m; m = m->next)
553 ipcacheStatPrint((ipcache_entry *)m->data, sentry);
554 }
555
556 static void
557 dummy_handler(const ipcache_addrs * addrsnotused, void *datanotused)
558 {
559 return;
560 }
561
562 void
563 ipcacheInvalidate(const char *name)
564 {
565 ipcache_entry *i;
566 if ((i = ipcache_get(name)) == NULL)
567 return;
568 i->expires = squid_curtime;
569 /*
570 * NOTE, don't call ipcacheRelease here becuase we might be here due
571 * to a thread started from a callback.
572 */
573 }
574
575 ipcache_addrs *
576 ipcacheCheckNumeric(const char *name)
577 {
578 struct in_addr ip;
579 /* check if it's already a IP address in text form. */
580 if (!safe_inet_addr(name, &ip))
581 return NULL;
582 static_addrs.count = 1;
583 static_addrs.cur = 0;
584 static_addrs.in_addrs[0].s_addr = ip.s_addr;
585 static_addrs.bad_mask[0] = FALSE;
586 static_addrs.badcount = 0;
587 return &static_addrs;
588 }
589
590 static void
591 ipcacheLockEntry(ipcache_entry * i)
592 {
593 if (i->locks++ == 0) {
594 dlinkDelete(&i->lru, &lru_list);
595 dlinkAdd(i, &i->lru, &lru_list);
596 }
597 }
598
599 static void
600 ipcacheUnlockEntry(ipcache_entry * i)
601 {
602 assert(i->locks > 0);
603 i->locks--;
604 if (ipcacheExpiredEntry(i))
605 ipcacheRelease(i);
606 }
607
608 void
609 ipcacheCycleAddr(const char *name, ipcache_addrs * ia)
610 {
611 ipcache_entry *i;
612 unsigned char k;
613 assert(name || ia);
614 if (NULL == ia) {
615 if ((i = ipcache_get(name)) == NULL)
616 return;
617 if (i->flags.negcached)
618 return;
619 ia = &i->addrs;
620 }
621 for (k = 0; k < ia->count; k++) {
622 if (++ia->cur == ia->count)
623 ia->cur = 0;
624 if (!ia->bad_mask[ia->cur])
625 break;
626 }
627 if (k == ia->count) {
628 /* All bad, reset to All good */
629 debug(14, 3) ("ipcacheCycleAddr: Changing ALL %s addrs from BAD to OK\n",
630 name);
631 for (k = 0; k < ia->count; k++)
632 ia->bad_mask[k] = 0;
633 ia->badcount = 0;
634 ia->cur = 0;
635 }
636 debug(14, 3) ("ipcacheCycleAddr: %s now at %s\n", name,
637 inet_ntoa(ia->in_addrs[ia->cur]));
638 }
639
640 /*
641 * Marks the given address as BAD and calls ipcacheCycleAddr to
642 * advance the current pointer to the next OK address.
643 */
644 void
645 ipcacheMarkBadAddr(const char *name, struct in_addr addr)
646 {
647 ipcache_entry *i;
648 ipcache_addrs *ia;
649 int k;
650 if ((i = ipcache_get(name)) == NULL)
651 return;
652 ia = &i->addrs;
653 for (k = 0; k < (int) ia->count; k++) {
654 if (ia->in_addrs[k].s_addr == addr.s_addr)
655 break;
656 }
657 if (k == (int) ia->count) /* not found */
658 return;
659 if (!ia->bad_mask[k]) {
660 ia->bad_mask[k] = TRUE;
661 ia->badcount++;
662 debug(14, 2) ("ipcacheMarkBadAddr: %s [%s]\n", name, inet_ntoa(addr));
663 }
664 ipcacheCycleAddr(name, ia);
665 }
666
667 void
668 ipcacheMarkGoodAddr(const char *name, struct in_addr addr)
669 {
670 ipcache_entry *i;
671 ipcache_addrs *ia;
672 int k;
673 if ((i = ipcache_get(name)) == NULL)
674 return;
675 ia = &i->addrs;
676 for (k = 0; k < (int) ia->count; k++) {
677 if (ia->in_addrs[k].s_addr == addr.s_addr)
678 break;
679 }
680 if (k == (int) ia->count) /* not found */
681 return;
682 if (!ia->bad_mask[k]) /* already OK */
683 return;
684 ia->bad_mask[k] = FALSE;
685 ia->badcount--;
686 debug(14, 2) ("ipcacheMarkGoodAddr: %s [%s]\n", name, inet_ntoa(addr));
687 }
688
689 static void
690 ipcacheFreeEntry(void *data)
691 {
692 ipcache_entry *i = (ipcache_entry *)data;
693 safe_free(i->addrs.in_addrs);
694 safe_free(i->addrs.bad_mask);
695 safe_free(i->hash.key);
696 safe_free(i->error_message);
697 memFree(i, MEM_IPCACHE_ENTRY);
698 }
699
700 void
701 ipcacheFreeMemory(void)
702 {
703 hashFreeItems(ip_table, ipcacheFreeEntry);
704 hashFreeMemory(ip_table);
705 ip_table = NULL;
706 }
707
708 /* Recalculate IP cache size upon reconfigure */
709 void
710 ipcache_restart(void)
711 {
712 ipcache_high = (long) (((float) Config.ipcache.size *
713 (float) Config.ipcache.high) / (float) 100);
714 ipcache_low = (long) (((float) Config.ipcache.size *
715 (float) Config.ipcache.low) / (float) 100);
716 purge_entries_fromhosts();
717 }
718
719 /*
720 * adds a "static" entry from /etc/hosts.
721 * returns 0 upon success, 1 if the ip address is invalid
722 */
723 int
724 ipcacheAddEntryFromHosts(const char *name, const char *ipaddr)
725 {
726 ipcache_entry *i;
727 struct in_addr ip;
728 if (!safe_inet_addr(ipaddr, &ip)) {
729 if (strchr(ipaddr, ':') && strspn(ipaddr, "0123456789abcdefABCDEF:") == strlen(ipaddr)) {
730 debug(14, 3) ("ipcacheAddEntryFromHosts: Skipping IPv6 address '%s'\n", ipaddr);
731 } else {
732 debug(14, 1) ("ipcacheAddEntryFromHosts: Bad IP address '%s'\n",
733 ipaddr);
734 }
735 return 1;
736 }
737 if ((i = ipcache_get(name))) {
738 if (1 == i->flags.fromhosts) {
739 ipcacheUnlockEntry(i);
740 } else if (i->locks > 0) {
741 debug(14, 1) ("ipcacheAddEntryFromHosts: can't add static entry"
742 " for locked name '%s'\n", name);
743 return 1;
744 } else {
745 ipcacheRelease(i);
746 }
747 }
748 i = ipcacheCreateEntry(name);
749 i->addrs.count = 1;
750 i->addrs.cur = 0;
751 i->addrs.badcount = 0;
752 i->addrs.in_addrs = (struct in_addr *)xcalloc(1, sizeof(struct in_addr));
753 i->addrs.bad_mask = (unsigned char *)xcalloc(1, sizeof(unsigned char));
754 i->addrs.in_addrs[0].s_addr = ip.s_addr;
755 i->addrs.bad_mask[0] = FALSE;
756 i->flags.fromhosts = 1;
757 ipcacheAddEntry(i);
758 ipcacheLockEntry(i);
759 return 0;
760 }
761
762 #ifdef SQUID_SNMP
763 /*
764 * The function to return the ip cache statistics to via SNMP
765 */
766
767 variable_list *
768 snmp_netIpFn(variable_list * Var, snint * ErrP)
769 {
770 variable_list *Answer = NULL;
771 debug(49, 5) ("snmp_netIpFn: Processing request:\n");
772 snmpDebugOid(5, Var->name, Var->name_length);
773 *ErrP = SNMP_ERR_NOERROR;
774 switch (Var->name[LEN_SQ_NET + 1]) {
775 case IP_ENT:
776 Answer = snmp_var_new_integer(Var->name, Var->name_length,
777 memInUse(MEM_IPCACHE_ENTRY),
778 SMI_GAUGE32);
779 break;
780 case IP_REQ:
781 Answer = snmp_var_new_integer(Var->name, Var->name_length,
782 IpcacheStats.requests,
783 SMI_COUNTER32);
784 break;
785 case IP_HITS:
786 Answer = snmp_var_new_integer(Var->name, Var->name_length,
787 IpcacheStats.hits,
788 SMI_COUNTER32);
789 break;
790 case IP_PENDHIT:
791 Answer = snmp_var_new_integer(Var->name, Var->name_length,
792 0,
793 SMI_GAUGE32);
794 break;
795 case IP_NEGHIT:
796 Answer = snmp_var_new_integer(Var->name, Var->name_length,
797 IpcacheStats.negative_hits,
798 SMI_COUNTER32);
799 break;
800 case IP_MISS:
801 Answer = snmp_var_new_integer(Var->name, Var->name_length,
802 IpcacheStats.misses,
803 SMI_COUNTER32);
804 break;
805 case IP_GHBN:
806 Answer = snmp_var_new_integer(Var->name, Var->name_length,
807 IpcacheStats.ghbn_calls,
808 SMI_COUNTER32);
809 break;
810 case IP_LOC:
811 Answer = snmp_var_new_integer(Var->name, Var->name_length,
812 IpcacheStats.release_locked,
813 SMI_COUNTER32);
814 break;
815 default:
816 *ErrP = SNMP_ERR_NOSUCHNAME;
817 snmp_var_free(Answer);
818 return (NULL);
819 }
820 return Answer;
821 }
822
823 #endif /*SQUID_SNMP */