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