]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ipcache.cc
Add a CacheManager class which provides the cachemanager menu registration facility...
[thirdparty/squid.git] / src / ipcache.cc
1
2 /*
3 * $Id: ipcache.cc,v 1.255 2006/05/29 00:15:02 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 "CacheManager.h"
38 #include "SquidTime.h"
39 #include "Store.h"
40 #include "wordlist.h"
41
42 typedef struct _ipcache_entry ipcache_entry;
43
44 struct _ipcache_entry
45 {
46 hash_link hash; /* must be first */
47 time_t lastref;
48 time_t expires;
49 ipcache_addrs addrs;
50 IPH *handler;
51 void *handlerData;
52 char *error_message;
53
54 struct timeval request_time;
55 dlink_node lru;
56 unsigned short locks;
57
58 struct
59 {
60
61 unsigned int negcached:
62 1;
63
64 unsigned int fromhosts:
65 1;
66 }
67
68 flags;
69 };
70
71 static struct
72 {
73 int requests;
74 int replies;
75 int hits;
76 int misses;
77 int negative_hits;
78 }
79
80 IpcacheStats;
81
82 static dlink_list lru_list;
83
84 static FREE ipcacheFreeEntry;
85 #if USE_DNSSERVERS
86 static HLPCB ipcacheHandleReply;
87 #else
88 static IDNSCB ipcacheHandleReply;
89 #endif
90 static IPH dummy_handler;
91 static int ipcacheExpiredEntry(ipcache_entry *);
92 static int ipcache_testname(void);
93 #if USE_DNSSERVERS
94 static int ipcacheParse(ipcache_entry *, const char *buf);
95 #else
96 static int ipcacheParse(ipcache_entry *, rfc1035_rr *, int, const char *error);
97 #endif
98 static ipcache_entry *ipcache_get(const char *);
99 static void ipcacheLockEntry(ipcache_entry *);
100 static void ipcacheStatPrint(ipcache_entry *, StoreEntry *);
101 static void ipcacheUnlockEntry(ipcache_entry *);
102 static void ipcacheRelease(ipcache_entry *);
103
104 static ipcache_addrs static_addrs;
105 static hash_table *ip_table = NULL;
106
107 static long ipcache_low = 180;
108 static long ipcache_high = 200;
109
110 #if LIBRESOLV_DNS_TTL_HACK
111 extern int _dns_ttl_;
112 #endif
113
114 static int
115 ipcache_testname(void)
116 {
117 wordlist *w = NULL;
118 debug(14, 1) ("Performing DNS Tests...\n");
119
120 if ((w = Config.dns_testname_list) == NULL)
121 return 1;
122
123 for (; w; w = w->next) {
124 if (gethostbyname(w->key) != NULL)
125 return 1;
126 }
127
128 return 0;
129 }
130
131 /* removes the given ipcache entry */
132 static void
133 ipcacheRelease(ipcache_entry * i)
134 {
135 debug(14, 3) ("ipcacheRelease: Releasing entry for '%s'\n", (const char *) i->hash.key);
136 hash_remove_link(ip_table, (hash_link *) i);
137 dlinkDelete(&i->lru, &lru_list);
138 ipcacheFreeEntry(i);
139 }
140
141 static ipcache_entry *
142 ipcache_get(const char *name)
143 {
144 if (ip_table != NULL)
145 return (ipcache_entry *) hash_lookup(ip_table, name);
146 else
147 return NULL;
148 }
149
150 static int
151 ipcacheExpiredEntry(ipcache_entry * i)
152 {
153 /* all static entries are locked, so this takes care of them too */
154
155 if (i->locks != 0)
156 return 0;
157
158 if (i->addrs.count == 0)
159 if (0 == i->flags.negcached)
160 return 1;
161
162 if (i->expires > squid_curtime)
163 return 0;
164
165 return 1;
166 }
167
168 void
169 ipcache_purgelru(void *voidnotused)
170 {
171 dlink_node *m;
172 dlink_node *prev = NULL;
173 ipcache_entry *i;
174 int removed = 0;
175 eventAdd("ipcache_purgelru", ipcache_purgelru, NULL, 10.0, 1);
176
177 for (m = lru_list.tail; m; m = prev) {
178 if (memInUse(MEM_IPCACHE_ENTRY) < ipcache_low)
179 break;
180
181 prev = m->prev;
182
183 i = (ipcache_entry *)m->data;
184
185 if (i->locks != 0)
186 continue;
187
188 ipcacheRelease(i);
189
190 removed++;
191 }
192
193 debug(14, 9) ("ipcache_purgelru: removed %d entries\n", removed);
194 }
195
196 /* purges entries added from /etc/hosts (or whatever). */
197 static void
198 purge_entries_fromhosts(void)
199 {
200 dlink_node *m = lru_list.head;
201 ipcache_entry *i = NULL, *t;
202
203 while (m) {
204 if (i != NULL) { /* need to delay deletion */
205 ipcacheRelease(i); /* we just override locks */
206 i = NULL;
207 }
208
209 t = (ipcache_entry*)m->data;
210
211 if (t->flags.fromhosts)
212 i = t;
213
214 m = m->next;
215 }
216
217 if (i != NULL)
218 ipcacheRelease(i);
219 }
220
221 /* create blank ipcache_entry */
222 static ipcache_entry *
223 ipcacheCreateEntry(const char *name)
224 {
225 static ipcache_entry *i;
226 i = (ipcache_entry *)memAllocate(MEM_IPCACHE_ENTRY);
227 i->hash.key = xstrdup(name);
228 i->expires = squid_curtime + Config.negativeDnsTtl;
229 return i;
230 }
231
232 static void
233 ipcacheAddEntry(ipcache_entry * i)
234 {
235 hash_link *e = (hash_link *)hash_lookup(ip_table, i->hash.key);
236
237 if (NULL != e) {
238 /* avoid colission */
239 ipcache_entry *q = (ipcache_entry *) e;
240 ipcacheRelease(q);
241 }
242
243 hash_join(ip_table, &i->hash);
244 dlinkAdd(i, &i->lru, &lru_list);
245 i->lastref = squid_curtime;
246 }
247
248 /* walks down the pending list, calling handlers */
249 static void
250 ipcacheCallback(ipcache_entry * i)
251 {
252 IPH *callback = i->handler;
253 void *cbdata;
254 i->lastref = squid_curtime;
255
256 if (!i->handler)
257 return;
258
259 ipcacheLockEntry(i);
260
261 callback = i->handler;
262
263 i->handler = NULL;
264
265 if (cbdataReferenceValidDone(i->handlerData, &cbdata)) {
266 dns_error_message = i->error_message;
267 callback(i->flags.negcached ? NULL : &i->addrs, cbdata);
268 }
269
270 ipcacheUnlockEntry(i);
271 }
272
273 #if USE_DNSSERVERS
274 static int
275 ipcacheParse(ipcache_entry *i, const char *inbuf)
276 {
277 LOCAL_ARRAY(char, buf, DNS_INBUF_SZ);
278 char *token;
279 int ipcount = 0;
280 int ttl;
281 char *A[32];
282 const char *name = (const char *)i->hash.key;
283 i->expires = squid_curtime + Config.negativeDnsTtl;
284 i->flags.negcached = 1;
285 safe_free(i->addrs.in_addrs);
286 safe_free(i->addrs.bad_mask);
287 safe_free(i->error_message);
288 i->addrs.count = 0;
289
290 if (inbuf == NULL) {
291 debug(14, 1) ("ipcacheParse: Got <NULL> reply\n");
292 i->error_message = xstrdup("Internal Error");
293 return -1;
294 }
295
296 xstrncpy(buf, inbuf, DNS_INBUF_SZ);
297 debug(14, 5) ("ipcacheParse: parsing: {%s}\n", buf);
298 token = strtok(buf, w_space);
299
300 if (NULL == token) {
301 debug(14, 1) ("ipcacheParse: expecting result, got '%s'\n", inbuf);
302 i->error_message = xstrdup("Internal Error");
303 return -1;
304 }
305
306 if (0 == strcmp(token, "$fail")) {
307 token = strtok(NULL, "\n");
308 assert(NULL != token);
309 i->error_message = xstrdup(token);
310 return 0;
311 }
312
313 if (0 != strcmp(token, "$addr")) {
314 debug(14, 1) ("ipcacheParse: expecting '$addr', got '%s' in response to '%s'\n", inbuf, name);
315 i->error_message = xstrdup("Internal Error");
316 return -1;
317 }
318
319 token = strtok(NULL, w_space);
320
321 if (NULL == token) {
322 debug(14, 1) ("ipcacheParse: expecting TTL, got '%s' in response to '%s'\n", inbuf, name);
323 i->error_message = xstrdup("Internal Error");
324 return -1;
325 }
326
327 ttl = atoi(token);
328
329 while (NULL != (token = strtok(NULL, w_space))) {
330 A[ipcount] = token;
331
332 if (++ipcount == 32)
333 break;
334 }
335
336 if (ipcount > 0) {
337 int j, k;
338
339 i->addrs.in_addrs = (struct IN_ADDR *)xcalloc(ipcount, sizeof(struct IN_ADDR));
340 i->addrs.bad_mask = (unsigned char *)xcalloc(ipcount, sizeof(unsigned char));
341
342 for (j = 0, k = 0; k < ipcount; k++) {
343 if (safe_inet_addr(A[k], &i->addrs.in_addrs[j]))
344 j++;
345 else
346 debug(14, 1) ("ipcacheParse: Invalid IP address '%s' in response to '%s'\n", A[k], name);
347 }
348
349 i->addrs.count = (unsigned char) j;
350 }
351
352 if (i->addrs.count <= 0) {
353 debug(14, 1) ("ipcacheParse: No addresses in response to '%s'\n", name);
354 return -1;
355 }
356
357 if (ttl == 0 || ttl > Config.positiveDnsTtl)
358 ttl = Config.positiveDnsTtl;
359
360 if (ttl < Config.negativeDnsTtl)
361 ttl = Config.negativeDnsTtl;
362
363 i->expires = squid_curtime + ttl;
364
365 i->flags.negcached = 0;
366
367 return i->addrs.count;
368 }
369
370 #else
371 static int
372 ipcacheParse(ipcache_entry *i, rfc1035_rr * answers, int nr, const char *error_message)
373 {
374 int k;
375 int j;
376 int na = 0;
377 int ttl = 0;
378 const char *name = (const char *)i->hash.key;
379 i->expires = squid_curtime + Config.negativeDnsTtl;
380 i->flags.negcached = 1;
381 safe_free(i->addrs.in_addrs);
382 safe_free(i->addrs.bad_mask);
383 safe_free(i->error_message);
384 i->addrs.count = 0;
385
386 if (nr < 0) {
387 debug(14, 3) ("ipcacheParse: Lookup failed '%s' for '%s'\n",
388 error_message, (const char *)i->hash.key);
389 i->error_message = xstrdup(error_message);
390 return -1;
391 }
392
393 if (nr == 0) {
394 debug(14, 3) ("ipcacheParse: No DNS records in response to '%s'\n", name);
395 i->error_message = xstrdup("No DNS records");
396 return 0;
397 }
398
399 assert(answers);
400
401 for (k = 0; k < nr; k++) {
402 if (answers[k].type != RFC1035_TYPE_A)
403 continue;
404
405 if (answers[k]._class != RFC1035_CLASS_IN)
406 continue;
407
408 if (answers[k].rdlength != 4) {
409 debug(14, 1)("ipcacheParse: Invalid IP address in response to '%s'\n", name);
410 continue;
411 }
412
413 na++;
414 }
415
416 if (na == 0) {
417 debug(14, 1) ("ipcacheParse: No Address records in response to '%s'\n", name);
418 i->error_message = xstrdup("No Address records");
419 return 0;
420 }
421
422 i->addrs.in_addrs = (struct IN_ADDR *)xcalloc(na, sizeof(struct IN_ADDR));
423 i->addrs.bad_mask = (unsigned char *)xcalloc(na, sizeof(unsigned char));
424
425 for (j = 0, k = 0; k < nr; k++) {
426 if (answers[k]._class != RFC1035_CLASS_IN)
427 continue;
428
429 if (answers[k].type == RFC1035_TYPE_A) {
430 if (answers[k].rdlength != 4)
431 continue;
432
433 xmemcpy(&i->addrs.in_addrs[j++], answers[k].rdata, 4);
434
435 debug(14, 3) ("ipcacheParse: #%d %s\n",
436 j - 1,
437 inet_ntoa(i->addrs.in_addrs[j - 1]));
438 } else if (answers[k].type != RFC1035_TYPE_CNAME)
439 continue;
440
441 if (ttl == 0 || (int) answers[k].ttl < ttl)
442 ttl = answers[k].ttl;
443
444 }
445
446 assert(j == na);
447
448 i->addrs.count = (unsigned char) na;
449
450 if (ttl == 0 || ttl > Config.positiveDnsTtl)
451 ttl = Config.positiveDnsTtl;
452
453 if (ttl < Config.negativeDnsTtl)
454 ttl = Config.negativeDnsTtl;
455
456 i->expires = squid_curtime + ttl;
457
458 i->flags.negcached = 0;
459
460 return i->addrs.count;
461 }
462
463 #endif
464
465 static void
466 #if USE_DNSSERVERS
467 ipcacheHandleReply(void *data, char *reply)
468 #else
469 ipcacheHandleReply(void *data, rfc1035_rr * answers, int na, const char *error_message)
470 #endif
471 {
472 generic_cbdata *c = (generic_cbdata *)data;
473 ipcache_entry *i = (ipcache_entry *)c->data;
474 cbdataFree(c);
475 c = NULL;
476 IpcacheStats.replies++;
477 statHistCount(&statCounter.dns.svc_time,
478 tvSubMsec(i->request_time, current_time));
479 #if USE_DNSSERVERS
480
481 ipcacheParse(i, reply);
482 #else
483
484 ipcacheParse(i, answers, na, error_message);
485 #endif
486
487 ipcacheAddEntry(i);
488 ipcacheCallback(i);
489 }
490
491 void
492 ipcache_nbgethostbyname(const char *name, IPH * handler, void *handlerData)
493 {
494 ipcache_entry *i = NULL;
495 const ipcache_addrs *addrs = NULL;
496 generic_cbdata *c;
497 assert(handler != NULL);
498 debug(14, 4) ("ipcache_nbgethostbyname: Name '%s'.\n", name);
499 IpcacheStats.requests++;
500
501 if (name == NULL || name[0] == '\0') {
502 debug(14, 4) ("ipcache_nbgethostbyname: Invalid name!\n");
503 dns_error_message = "Invalid hostname";
504 handler(NULL, handlerData);
505 return;
506 }
507
508 if ((addrs = ipcacheCheckNumeric(name))) {
509 dns_error_message = NULL;
510 handler(addrs, handlerData);
511 return;
512 }
513
514 i = ipcache_get(name);
515
516 if (NULL == i) {
517 /* miss */
518 (void) 0;
519 } else if (ipcacheExpiredEntry(i)) {
520 /* hit, but expired -- bummer */
521 ipcacheRelease(i);
522 i = NULL;
523 } else {
524 /* hit */
525 debug(14, 4) ("ipcache_nbgethostbyname: HIT for '%s'\n", name);
526
527 if (i->flags.negcached)
528 IpcacheStats.negative_hits++;
529 else
530 IpcacheStats.hits++;
531
532 i->handler = handler;
533
534 i->handlerData = cbdataReference(handlerData);
535
536 ipcacheCallback(i);
537
538 return;
539 }
540
541 debug(14, 5) ("ipcache_nbgethostbyname: MISS for '%s'\n", name);
542 IpcacheStats.misses++;
543 i = ipcacheCreateEntry(name);
544 i->handler = handler;
545 i->handlerData = cbdataReference(handlerData);
546 i->request_time = current_time;
547 c = cbdataAlloc(generic_cbdata);
548 c->data = i;
549 #if USE_DNSSERVERS
550
551 dnsSubmit(hashKeyStr(&i->hash), ipcacheHandleReply, c);
552 #else
553
554 idnsALookup(hashKeyStr(&i->hash), ipcacheHandleReply, c);
555 #endif
556 }
557
558 /* initialize the ipcache */
559 void
560 ipcache_init(void)
561 {
562 int n;
563 debug(14, 3) ("Initializing IP Cache...\n");
564 memset(&IpcacheStats, '\0', sizeof(IpcacheStats));
565 memset(&lru_list, '\0', sizeof(lru_list));
566 /* test naming lookup */
567
568 if (!opt_dns_tests) {
569 debug(14, 4) ("ipcache_init: Skipping DNS name lookup tests.\n");
570 } else if (!ipcache_testname()) {
571 fatal("ipcache_init: DNS name lookup tests failed.");
572 } else {
573 debug(14, 1) ("Successful DNS name lookup tests...\n");
574 }
575
576 memset(&static_addrs, '\0', sizeof(ipcache_addrs));
577
578 static_addrs.in_addrs = (struct IN_ADDR *)xcalloc(1, sizeof(struct IN_ADDR));
579 static_addrs.bad_mask = (unsigned char *)xcalloc(1, sizeof(unsigned char));
580 ipcache_high = (long) (((float) Config.ipcache.size *
581 (float) Config.ipcache.high) / (float) 100);
582 ipcache_low = (long) (((float) Config.ipcache.size *
583 (float) Config.ipcache.low) / (float) 100);
584 n = hashPrime(ipcache_high / 4);
585 ip_table = hash_create((HASHCMP *) strcmp, n, hash4);
586 memDataInit(MEM_IPCACHE_ENTRY, "ipcache_entry", sizeof(ipcache_entry), 0);
587 }
588
589 void
590 ipcacheRegisterWithCacheManager(CacheManager & manager)
591 {
592 manager.registerAction("ipcache",
593 "IP Cache Stats and Contents",
594 stat_ipcache_get, 0, 1);
595 }
596
597 const ipcache_addrs *
598 ipcache_gethostbyname(const char *name, int flags)
599 {
600 ipcache_entry *i = NULL;
601 ipcache_addrs *addrs;
602 assert(name);
603 debug(14, 3) ("ipcache_gethostbyname: '%s', flags=%x\n", name, flags);
604 IpcacheStats.requests++;
605 i = ipcache_get(name);
606
607 if (NULL == i) {
608 (void) 0;
609 } else if (ipcacheExpiredEntry(i)) {
610 ipcacheRelease(i);
611 i = NULL;
612 } else if (i->flags.negcached) {
613 IpcacheStats.negative_hits++;
614 dns_error_message = i->error_message;
615 return NULL;
616 } else {
617 IpcacheStats.hits++;
618 i->lastref = squid_curtime;
619 dns_error_message = i->error_message;
620 return &i->addrs;
621 }
622
623 dns_error_message = NULL;
624
625 if ((addrs = ipcacheCheckNumeric(name)))
626 return addrs;
627
628 IpcacheStats.misses++;
629
630 if (flags & IP_LOOKUP_IF_MISS)
631 ipcache_nbgethostbyname(name, dummy_handler, NULL);
632
633 return NULL;
634 }
635
636 static void
637 ipcacheStatPrint(ipcache_entry * i, StoreEntry * sentry)
638 {
639 int k;
640 storeAppendPrintf(sentry, " %-32.32s %c%c %6d %6d %2d(%2d)",
641 hashKeyStr(&i->hash),
642 i->flags.fromhosts ? 'H' : ' ',
643 i->flags.negcached ? 'N' : ' ',
644 (int) (squid_curtime - i->lastref),
645 (int) ((i->flags.fromhosts ? -1 : i->expires - squid_curtime)),
646 (int) i->addrs.count,
647 (int) i->addrs.badcount);
648
649 for (k = 0; k < (int) i->addrs.count; k++) {
650 storeAppendPrintf(sentry, " %15s-%3s", inet_ntoa(i->addrs.in_addrs[k]),
651 i->addrs.bad_mask[k] ? "BAD" : "OK ");
652 }
653
654 storeAppendPrintf(sentry, "\n");
655 }
656
657 /* process objects list */
658 void
659 stat_ipcache_get(StoreEntry * sentry)
660 {
661 dlink_node *m;
662 assert(ip_table != NULL);
663 storeAppendPrintf(sentry, "IP Cache Statistics:\n");
664 storeAppendPrintf(sentry, "IPcache Entries: %d\n",
665 memInUse(MEM_IPCACHE_ENTRY));
666 storeAppendPrintf(sentry, "IPcache Requests: %d\n",
667 IpcacheStats.requests);
668 storeAppendPrintf(sentry, "IPcache Hits: %d\n",
669 IpcacheStats.hits);
670 storeAppendPrintf(sentry, "IPcache Negative Hits: %d\n",
671 IpcacheStats.negative_hits);
672 storeAppendPrintf(sentry, "IPcache Misses: %d\n",
673 IpcacheStats.misses);
674 storeAppendPrintf(sentry, "\n\n");
675 storeAppendPrintf(sentry, "IP Cache Contents:\n\n");
676 storeAppendPrintf(sentry, " %-29.29s %3s %6s %6s %1s\n",
677 "Hostname",
678 "Flg",
679 "lstref",
680 "TTL",
681 "N");
682
683 for (m = lru_list.head; m; m = m->next)
684 ipcacheStatPrint((ipcache_entry *)m->data, sentry);
685 }
686
687 static void
688 dummy_handler(const ipcache_addrs * addrsnotused, void *datanotused)
689 {
690 return;
691 }
692
693 void
694 ipcacheInvalidate(const char *name)
695 {
696 ipcache_entry *i;
697
698 if ((i = ipcache_get(name)) == NULL)
699 return;
700
701 i->expires = squid_curtime;
702
703 /*
704 * NOTE, don't call ipcacheRelease here becuase we might be here due
705 * to a thread started from a callback.
706 */
707 }
708
709 void
710 ipcacheInvalidateNegative(const char *name)
711 {
712 ipcache_entry *i;
713
714 if ((i = ipcache_get(name)) == NULL)
715 return;
716
717 if (i->flags.negcached)
718 i->expires = squid_curtime;
719
720 /*
721 * NOTE, don't call ipcacheRelease here becuase we might be here due
722 * to a thread started from a callback.
723 */
724 }
725
726 ipcache_addrs *
727 ipcacheCheckNumeric(const char *name)
728 {
729
730 struct IN_ADDR ip;
731 /* check if it's already a IP address in text form. */
732
733 if (!safe_inet_addr(name, &ip))
734 return NULL;
735
736 static_addrs.count = 1;
737
738 static_addrs.cur = 0;
739
740 static_addrs.in_addrs[0].s_addr = ip.s_addr;
741
742 static_addrs.bad_mask[0] = FALSE;
743
744 static_addrs.badcount = 0;
745
746 return &static_addrs;
747 }
748
749 static void
750 ipcacheLockEntry(ipcache_entry * i)
751 {
752 if (i->locks++ == 0) {
753 dlinkDelete(&i->lru, &lru_list);
754 dlinkAdd(i, &i->lru, &lru_list);
755 }
756 }
757
758 static void
759 ipcacheUnlockEntry(ipcache_entry * i)
760 {
761 assert(i->locks > 0);
762 i->locks--;
763
764 if (ipcacheExpiredEntry(i))
765 ipcacheRelease(i);
766 }
767
768 void
769 ipcacheCycleAddr(const char *name, ipcache_addrs * ia)
770 {
771 ipcache_entry *i;
772 unsigned char k;
773 assert(name || ia);
774
775 if (NULL == ia) {
776 if ((i = ipcache_get(name)) == NULL)
777 return;
778
779 if (i->flags.negcached)
780 return;
781
782 ia = &i->addrs;
783 }
784
785 for (k = 0; k < ia->count; k++) {
786 if (++ia->cur == ia->count)
787 ia->cur = 0;
788
789 if (!ia->bad_mask[ia->cur])
790 break;
791 }
792
793 if (k == ia->count) {
794 /* All bad, reset to All good */
795 debug(14, 3) ("ipcacheCycleAddr: Changing ALL %s addrs from BAD to OK\n",
796 name);
797
798 for (k = 0; k < ia->count; k++)
799 ia->bad_mask[k] = 0;
800
801 ia->badcount = 0;
802
803 ia->cur = 0;
804 }
805
806 debug(14, 3) ("ipcacheCycleAddr: %s now at %s\n", name,
807 inet_ntoa(ia->in_addrs[ia->cur]));
808 }
809
810 /*
811 * Marks the given address as BAD and calls ipcacheCycleAddr to
812 * advance the current pointer to the next OK address.
813 */
814 void
815
816 ipcacheMarkBadAddr(const char *name, struct IN_ADDR addr)
817 {
818 ipcache_entry *i;
819 ipcache_addrs *ia;
820 int k;
821
822 if ((i = ipcache_get(name)) == NULL)
823 return;
824
825 ia = &i->addrs;
826
827 for (k = 0; k < (int) ia->count; k++)
828 {
829 if (ia->in_addrs[k].s_addr == addr.s_addr)
830 break;
831 }
832
833 if (k == (int) ia->count) /* not found */
834 return;
835
836 if (!ia->bad_mask[k])
837 {
838 ia->bad_mask[k] = TRUE;
839 ia->badcount++;
840 i->expires = XMIN(squid_curtime + XMAX((time_t)60, Config.negativeDnsTtl), i->expires);
841 debug(14, 2) ("ipcacheMarkBadAddr: %s [%s]\n", name, inet_ntoa(addr));
842 }
843
844 ipcacheCycleAddr(name, ia);
845 }
846
847 void
848
849 ipcacheMarkGoodAddr(const char *name, struct IN_ADDR addr)
850 {
851 ipcache_entry *i;
852 ipcache_addrs *ia;
853 int k;
854
855 if ((i = ipcache_get(name)) == NULL)
856 return;
857
858 ia = &i->addrs;
859
860 for (k = 0; k < (int) ia->count; k++)
861 {
862 if (ia->in_addrs[k].s_addr == addr.s_addr)
863 break;
864 }
865
866 if (k == (int) ia->count) /* not found */
867 return;
868
869 if (!ia->bad_mask[k]) /* already OK */
870 return;
871
872 ia->bad_mask[k] = FALSE;
873
874 ia->badcount--;
875
876 debug(14, 2) ("ipcacheMarkGoodAddr: %s [%s]\n", name, inet_ntoa(addr));
877 }
878
879 static void
880 ipcacheFreeEntry(void *data)
881 {
882 ipcache_entry *i = (ipcache_entry *)data;
883 safe_free(i->addrs.in_addrs);
884 safe_free(i->addrs.bad_mask);
885 safe_free(i->hash.key);
886 safe_free(i->error_message);
887 memFree(i, MEM_IPCACHE_ENTRY);
888 }
889
890 void
891 ipcacheFreeMemory(void)
892 {
893 hashFreeItems(ip_table, ipcacheFreeEntry);
894 hashFreeMemory(ip_table);
895 ip_table = NULL;
896 }
897
898 /* Recalculate IP cache size upon reconfigure */
899 void
900 ipcache_restart(void)
901 {
902 ipcache_high = (long) (((float) Config.ipcache.size *
903 (float) Config.ipcache.high) / (float) 100);
904 ipcache_low = (long) (((float) Config.ipcache.size *
905 (float) Config.ipcache.low) / (float) 100);
906 purge_entries_fromhosts();
907 }
908
909 /*
910 * adds a "static" entry from /etc/hosts.
911 * returns 0 upon success, 1 if the ip address is invalid
912 */
913 int
914 ipcacheAddEntryFromHosts(const char *name, const char *ipaddr)
915 {
916 ipcache_entry *i;
917
918 struct IN_ADDR ip;
919
920 if (!safe_inet_addr(ipaddr, &ip)) {
921 if (strchr(ipaddr, ':') && strspn(ipaddr, "0123456789abcdefABCDEF:") == strlen(ipaddr)) {
922 debug(14, 3) ("ipcacheAddEntryFromHosts: Skipping IPv6 address '%s'\n", ipaddr);
923 } else {
924 debug(14, 1) ("ipcacheAddEntryFromHosts: Bad IP address '%s'\n",
925 ipaddr);
926 }
927
928 return 1;
929 }
930
931 if ((i = ipcache_get(name))) {
932 if (1 == i->flags.fromhosts) {
933 ipcacheUnlockEntry(i);
934 } else if (i->locks > 0) {
935 debug(14, 1) ("ipcacheAddEntryFromHosts: can't add static entry"
936 " for locked name '%s'\n", name);
937 return 1;
938 } else {
939 ipcacheRelease(i);
940 }
941 }
942
943 i = ipcacheCreateEntry(name);
944 i->addrs.count = 1;
945 i->addrs.cur = 0;
946 i->addrs.badcount = 0;
947
948 i->addrs.in_addrs = (struct IN_ADDR *)xcalloc(1, sizeof(struct IN_ADDR));
949 i->addrs.bad_mask = (unsigned char *)xcalloc(1, sizeof(unsigned char));
950 i->addrs.in_addrs[0].s_addr = ip.s_addr;
951 i->addrs.bad_mask[0] = FALSE;
952 i->flags.fromhosts = 1;
953 ipcacheAddEntry(i);
954 ipcacheLockEntry(i);
955 return 0;
956 }
957
958 #ifdef SQUID_SNMP
959 /*
960 * The function to return the ip cache statistics to via SNMP
961 */
962
963 variable_list *
964 snmp_netIpFn(variable_list * Var, snint * ErrP)
965 {
966 variable_list *Answer = NULL;
967 debug(49, 5) ("snmp_netIpFn: Processing request:\n");
968 snmpDebugOid(5, Var->name, Var->name_length);
969 *ErrP = SNMP_ERR_NOERROR;
970
971 switch (Var->name[LEN_SQ_NET + 1]) {
972
973 case IP_ENT:
974 Answer = snmp_var_new_integer(Var->name, Var->name_length,
975 memInUse(MEM_IPCACHE_ENTRY),
976 SMI_GAUGE32);
977 break;
978
979 case IP_REQ:
980 Answer = snmp_var_new_integer(Var->name, Var->name_length,
981 IpcacheStats.requests,
982 SMI_COUNTER32);
983 break;
984
985 case IP_HITS:
986 Answer = snmp_var_new_integer(Var->name, Var->name_length,
987 IpcacheStats.hits,
988 SMI_COUNTER32);
989 break;
990
991 case IP_PENDHIT:
992 Answer = snmp_var_new_integer(Var->name, Var->name_length,
993 0,
994 SMI_GAUGE32);
995 break;
996
997 case IP_NEGHIT:
998 Answer = snmp_var_new_integer(Var->name, Var->name_length,
999 IpcacheStats.negative_hits,
1000 SMI_COUNTER32);
1001 break;
1002
1003 case IP_MISS:
1004 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1005 IpcacheStats.misses,
1006 SMI_COUNTER32);
1007 break;
1008
1009 case IP_GHBN:
1010 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1011 0, /* deprecated */
1012 SMI_COUNTER32);
1013 break;
1014
1015 case IP_LOC:
1016 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1017 0, /* deprecated */
1018 SMI_COUNTER32);
1019 break;
1020
1021 default:
1022 *ErrP = SNMP_ERR_NOSUCHNAME;
1023 snmp_var_free(Answer);
1024 return (NULL);
1025 }
1026
1027 return Answer;
1028 }
1029
1030 #endif /*SQUID_SNMP */