]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ipcache.cc
Bug #1404: CNAME adresses remembered with wrong TTL
[thirdparty/squid.git] / src / ipcache.cc
1
2 /*
3 * $Id: ipcache.cc,v 1.252 2005/10/23 14:10:45 serassio 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]._class != RFC1035_CLASS_IN)
424 continue;
425
426 if (answers[k].type == RFC1035_TYPE_A) {
427 if (answers[k].rdlength != 4)
428 continue;
429
430 xmemcpy(&i->addrs.in_addrs[j++], answers[k].rdata, 4);
431
432 debug(14, 3) ("ipcacheParse: #%d %s\n",
433 j - 1,
434 inet_ntoa(i->addrs.in_addrs[j - 1]));
435 } else if (answers[k].type != RFC1035_TYPE_CNAME)
436 continue;
437
438 if (ttl == 0 || (int) answers[k].ttl < ttl)
439 ttl = answers[k].ttl;
440
441 }
442
443 assert(j == na);
444
445 i->addrs.count = (unsigned char) na;
446
447 if (ttl == 0 || ttl > Config.positiveDnsTtl)
448 ttl = Config.positiveDnsTtl;
449
450 if (ttl < Config.negativeDnsTtl)
451 ttl = Config.negativeDnsTtl;
452
453 i->expires = squid_curtime + ttl;
454
455 i->flags.negcached = 0;
456
457 return i->addrs.count;
458 }
459
460 #endif
461
462 static void
463 #if USE_DNSSERVERS
464 ipcacheHandleReply(void *data, char *reply)
465 #else
466 ipcacheHandleReply(void *data, rfc1035_rr * answers, int na, const char *error_message)
467 #endif
468 {
469 generic_cbdata *c = (generic_cbdata *)data;
470 ipcache_entry *i = (ipcache_entry *)c->data;
471 cbdataFree(c);
472 c = NULL;
473 IpcacheStats.replies++;
474 statHistCount(&statCounter.dns.svc_time,
475 tvSubMsec(i->request_time, current_time));
476 #if USE_DNSSERVERS
477
478 ipcacheParse(i, reply);
479 #else
480
481 ipcacheParse(i, answers, na, error_message);
482 #endif
483
484 ipcacheAddEntry(i);
485 ipcacheCallback(i);
486 }
487
488 void
489 ipcache_nbgethostbyname(const char *name, IPH * handler, void *handlerData)
490 {
491 ipcache_entry *i = NULL;
492 const ipcache_addrs *addrs = NULL;
493 generic_cbdata *c;
494 assert(handler != NULL);
495 debug(14, 4) ("ipcache_nbgethostbyname: Name '%s'.\n", name);
496 IpcacheStats.requests++;
497
498 if (name == NULL || name[0] == '\0') {
499 debug(14, 4) ("ipcache_nbgethostbyname: Invalid name!\n");
500 dns_error_message = "Invalid hostname";
501 handler(NULL, handlerData);
502 return;
503 }
504
505 if ((addrs = ipcacheCheckNumeric(name))) {
506 dns_error_message = NULL;
507 handler(addrs, handlerData);
508 return;
509 }
510
511 i = ipcache_get(name);
512
513 if (NULL == i) {
514 /* miss */
515 (void) 0;
516 } else if (ipcacheExpiredEntry(i)) {
517 /* hit, but expired -- bummer */
518 ipcacheRelease(i);
519 i = NULL;
520 } else {
521 /* hit */
522 debug(14, 4) ("ipcache_nbgethostbyname: HIT for '%s'\n", name);
523
524 if (i->flags.negcached)
525 IpcacheStats.negative_hits++;
526 else
527 IpcacheStats.hits++;
528
529 i->handler = handler;
530
531 i->handlerData = cbdataReference(handlerData);
532
533 ipcacheCallback(i);
534
535 return;
536 }
537
538 debug(14, 5) ("ipcache_nbgethostbyname: MISS for '%s'\n", name);
539 IpcacheStats.misses++;
540 i = ipcacheCreateEntry(name);
541 i->handler = handler;
542 i->handlerData = cbdataReference(handlerData);
543 i->request_time = current_time;
544 c = cbdataAlloc(generic_cbdata);
545 c->data = i;
546 #if USE_DNSSERVERS
547
548 dnsSubmit(hashKeyStr(&i->hash), ipcacheHandleReply, c);
549 #else
550
551 idnsALookup(hashKeyStr(&i->hash), ipcacheHandleReply, c);
552 #endif
553 }
554
555 /* initialize the ipcache */
556 void
557 ipcache_init(void)
558 {
559 int n;
560 debug(14, 3) ("Initializing IP Cache...\n");
561 memset(&IpcacheStats, '\0', sizeof(IpcacheStats));
562 memset(&lru_list, '\0', sizeof(lru_list));
563 /* test naming lookup */
564
565 if (!opt_dns_tests) {
566 debug(14, 4) ("ipcache_init: Skipping DNS name lookup tests.\n");
567 } else if (!ipcache_testname()) {
568 fatal("ipcache_init: DNS name lookup tests failed.");
569 } else {
570 debug(14, 1) ("Successful DNS name lookup tests...\n");
571 }
572
573 memset(&static_addrs, '\0', sizeof(ipcache_addrs));
574
575 static_addrs.in_addrs = (struct IN_ADDR *)xcalloc(1, sizeof(struct IN_ADDR));
576 static_addrs.bad_mask = (unsigned char *)xcalloc(1, sizeof(unsigned char));
577 ipcache_high = (long) (((float) Config.ipcache.size *
578 (float) Config.ipcache.high) / (float) 100);
579 ipcache_low = (long) (((float) Config.ipcache.size *
580 (float) Config.ipcache.low) / (float) 100);
581 n = hashPrime(ipcache_high / 4);
582 ip_table = hash_create((HASHCMP *) strcmp, n, hash4);
583 cachemgrRegister("ipcache",
584 "IP Cache Stats and Contents",
585 stat_ipcache_get, 0, 1);
586 memDataInit(MEM_IPCACHE_ENTRY, "ipcache_entry", sizeof(ipcache_entry), 0);
587 }
588
589 const ipcache_addrs *
590 ipcache_gethostbyname(const char *name, int flags)
591 {
592 ipcache_entry *i = NULL;
593 ipcache_addrs *addrs;
594 assert(name);
595 debug(14, 3) ("ipcache_gethostbyname: '%s', flags=%x\n", name, flags);
596 IpcacheStats.requests++;
597 i = ipcache_get(name);
598
599 if (NULL == i) {
600 (void) 0;
601 } else if (ipcacheExpiredEntry(i)) {
602 ipcacheRelease(i);
603 i = NULL;
604 } else if (i->flags.negcached) {
605 IpcacheStats.negative_hits++;
606 dns_error_message = i->error_message;
607 return NULL;
608 } else {
609 IpcacheStats.hits++;
610 i->lastref = squid_curtime;
611 dns_error_message = i->error_message;
612 return &i->addrs;
613 }
614
615 dns_error_message = NULL;
616
617 if ((addrs = ipcacheCheckNumeric(name)))
618 return addrs;
619
620 IpcacheStats.misses++;
621
622 if (flags & IP_LOOKUP_IF_MISS)
623 ipcache_nbgethostbyname(name, dummy_handler, NULL);
624
625 return NULL;
626 }
627
628 static void
629 ipcacheStatPrint(ipcache_entry * i, StoreEntry * sentry)
630 {
631 int k;
632 storeAppendPrintf(sentry, " %-32.32s %c%c %6d %6d %2d(%2d)",
633 hashKeyStr(&i->hash),
634 i->flags.fromhosts ? 'H' : ' ',
635 i->flags.negcached ? 'N' : ' ',
636 (int) (squid_curtime - i->lastref),
637 (int) ((i->flags.fromhosts ? -1 : i->expires - squid_curtime)),
638 (int) i->addrs.count,
639 (int) i->addrs.badcount);
640
641 for (k = 0; k < (int) i->addrs.count; k++) {
642 storeAppendPrintf(sentry, " %15s-%3s", inet_ntoa(i->addrs.in_addrs[k]),
643 i->addrs.bad_mask[k] ? "BAD" : "OK ");
644 }
645
646 storeAppendPrintf(sentry, "\n");
647 }
648
649 /* process objects list */
650 void
651 stat_ipcache_get(StoreEntry * sentry)
652 {
653 dlink_node *m;
654 assert(ip_table != NULL);
655 storeAppendPrintf(sentry, "IP Cache Statistics:\n");
656 storeAppendPrintf(sentry, "IPcache Entries: %d\n",
657 memInUse(MEM_IPCACHE_ENTRY));
658 storeAppendPrintf(sentry, "IPcache Requests: %d\n",
659 IpcacheStats.requests);
660 storeAppendPrintf(sentry, "IPcache Hits: %d\n",
661 IpcacheStats.hits);
662 storeAppendPrintf(sentry, "IPcache Negative Hits: %d\n",
663 IpcacheStats.negative_hits);
664 storeAppendPrintf(sentry, "IPcache Misses: %d\n",
665 IpcacheStats.misses);
666 storeAppendPrintf(sentry, "\n\n");
667 storeAppendPrintf(sentry, "IP Cache Contents:\n\n");
668 storeAppendPrintf(sentry, " %-29.29s %3s %6s %6s %1s\n",
669 "Hostname",
670 "Flg",
671 "lstref",
672 "TTL",
673 "N");
674
675 for (m = lru_list.head; m; m = m->next)
676 ipcacheStatPrint((ipcache_entry *)m->data, sentry);
677 }
678
679 static void
680 dummy_handler(const ipcache_addrs * addrsnotused, void *datanotused)
681 {
682 return;
683 }
684
685 void
686 ipcacheInvalidate(const char *name)
687 {
688 ipcache_entry *i;
689
690 if ((i = ipcache_get(name)) == NULL)
691 return;
692
693 i->expires = squid_curtime;
694
695 /*
696 * NOTE, don't call ipcacheRelease here becuase we might be here due
697 * to a thread started from a callback.
698 */
699 }
700
701 void
702 ipcacheInvalidateNegative(const char *name)
703 {
704 ipcache_entry *i;
705
706 if ((i = ipcache_get(name)) == NULL)
707 return;
708
709 if (i->flags.negcached)
710 i->expires = squid_curtime;
711
712 /*
713 * NOTE, don't call ipcacheRelease here becuase we might be here due
714 * to a thread started from a callback.
715 */
716 }
717
718 ipcache_addrs *
719 ipcacheCheckNumeric(const char *name)
720 {
721
722 struct IN_ADDR ip;
723 /* check if it's already a IP address in text form. */
724
725 if (!safe_inet_addr(name, &ip))
726 return NULL;
727
728 static_addrs.count = 1;
729
730 static_addrs.cur = 0;
731
732 static_addrs.in_addrs[0].s_addr = ip.s_addr;
733
734 static_addrs.bad_mask[0] = FALSE;
735
736 static_addrs.badcount = 0;
737
738 return &static_addrs;
739 }
740
741 static void
742 ipcacheLockEntry(ipcache_entry * i)
743 {
744 if (i->locks++ == 0) {
745 dlinkDelete(&i->lru, &lru_list);
746 dlinkAdd(i, &i->lru, &lru_list);
747 }
748 }
749
750 static void
751 ipcacheUnlockEntry(ipcache_entry * i)
752 {
753 assert(i->locks > 0);
754 i->locks--;
755
756 if (ipcacheExpiredEntry(i))
757 ipcacheRelease(i);
758 }
759
760 void
761 ipcacheCycleAddr(const char *name, ipcache_addrs * ia)
762 {
763 ipcache_entry *i;
764 unsigned char k;
765 assert(name || ia);
766
767 if (NULL == ia) {
768 if ((i = ipcache_get(name)) == NULL)
769 return;
770
771 if (i->flags.negcached)
772 return;
773
774 ia = &i->addrs;
775 }
776
777 for (k = 0; k < ia->count; k++) {
778 if (++ia->cur == ia->count)
779 ia->cur = 0;
780
781 if (!ia->bad_mask[ia->cur])
782 break;
783 }
784
785 if (k == ia->count) {
786 /* All bad, reset to All good */
787 debug(14, 3) ("ipcacheCycleAddr: Changing ALL %s addrs from BAD to OK\n",
788 name);
789
790 for (k = 0; k < ia->count; k++)
791 ia->bad_mask[k] = 0;
792
793 ia->badcount = 0;
794
795 ia->cur = 0;
796 }
797
798 debug(14, 3) ("ipcacheCycleAddr: %s now at %s\n", name,
799 inet_ntoa(ia->in_addrs[ia->cur]));
800 }
801
802 /*
803 * Marks the given address as BAD and calls ipcacheCycleAddr to
804 * advance the current pointer to the next OK address.
805 */
806 void
807
808 ipcacheMarkBadAddr(const char *name, struct IN_ADDR addr)
809 {
810 ipcache_entry *i;
811 ipcache_addrs *ia;
812 int k;
813
814 if ((i = ipcache_get(name)) == NULL)
815 return;
816
817 ia = &i->addrs;
818
819 for (k = 0; k < (int) ia->count; k++)
820 {
821 if (ia->in_addrs[k].s_addr == addr.s_addr)
822 break;
823 }
824
825 if (k == (int) ia->count) /* not found */
826 return;
827
828 if (!ia->bad_mask[k])
829 {
830 ia->bad_mask[k] = TRUE;
831 ia->badcount++;
832 i->expires = XMIN(squid_curtime + XMAX((time_t)60, Config.negativeDnsTtl), i->expires);
833 debug(14, 2) ("ipcacheMarkBadAddr: %s [%s]\n", name, inet_ntoa(addr));
834 }
835
836 ipcacheCycleAddr(name, ia);
837 }
838
839 void
840
841 ipcacheMarkGoodAddr(const char *name, struct IN_ADDR addr)
842 {
843 ipcache_entry *i;
844 ipcache_addrs *ia;
845 int k;
846
847 if ((i = ipcache_get(name)) == NULL)
848 return;
849
850 ia = &i->addrs;
851
852 for (k = 0; k < (int) ia->count; k++)
853 {
854 if (ia->in_addrs[k].s_addr == addr.s_addr)
855 break;
856 }
857
858 if (k == (int) ia->count) /* not found */
859 return;
860
861 if (!ia->bad_mask[k]) /* already OK */
862 return;
863
864 ia->bad_mask[k] = FALSE;
865
866 ia->badcount--;
867
868 debug(14, 2) ("ipcacheMarkGoodAddr: %s [%s]\n", name, inet_ntoa(addr));
869 }
870
871 static void
872 ipcacheFreeEntry(void *data)
873 {
874 ipcache_entry *i = (ipcache_entry *)data;
875 safe_free(i->addrs.in_addrs);
876 safe_free(i->addrs.bad_mask);
877 safe_free(i->hash.key);
878 safe_free(i->error_message);
879 memFree(i, MEM_IPCACHE_ENTRY);
880 }
881
882 void
883 ipcacheFreeMemory(void)
884 {
885 hashFreeItems(ip_table, ipcacheFreeEntry);
886 hashFreeMemory(ip_table);
887 ip_table = NULL;
888 }
889
890 /* Recalculate IP cache size upon reconfigure */
891 void
892 ipcache_restart(void)
893 {
894 ipcache_high = (long) (((float) Config.ipcache.size *
895 (float) Config.ipcache.high) / (float) 100);
896 ipcache_low = (long) (((float) Config.ipcache.size *
897 (float) Config.ipcache.low) / (float) 100);
898 purge_entries_fromhosts();
899 }
900
901 /*
902 * adds a "static" entry from /etc/hosts.
903 * returns 0 upon success, 1 if the ip address is invalid
904 */
905 int
906 ipcacheAddEntryFromHosts(const char *name, const char *ipaddr)
907 {
908 ipcache_entry *i;
909
910 struct IN_ADDR ip;
911
912 if (!safe_inet_addr(ipaddr, &ip)) {
913 if (strchr(ipaddr, ':') && strspn(ipaddr, "0123456789abcdefABCDEF:") == strlen(ipaddr)) {
914 debug(14, 3) ("ipcacheAddEntryFromHosts: Skipping IPv6 address '%s'\n", ipaddr);
915 } else {
916 debug(14, 1) ("ipcacheAddEntryFromHosts: Bad IP address '%s'\n",
917 ipaddr);
918 }
919
920 return 1;
921 }
922
923 if ((i = ipcache_get(name))) {
924 if (1 == i->flags.fromhosts) {
925 ipcacheUnlockEntry(i);
926 } else if (i->locks > 0) {
927 debug(14, 1) ("ipcacheAddEntryFromHosts: can't add static entry"
928 " for locked name '%s'\n", name);
929 return 1;
930 } else {
931 ipcacheRelease(i);
932 }
933 }
934
935 i = ipcacheCreateEntry(name);
936 i->addrs.count = 1;
937 i->addrs.cur = 0;
938 i->addrs.badcount = 0;
939
940 i->addrs.in_addrs = (struct IN_ADDR *)xcalloc(1, sizeof(struct IN_ADDR));
941 i->addrs.bad_mask = (unsigned char *)xcalloc(1, sizeof(unsigned char));
942 i->addrs.in_addrs[0].s_addr = ip.s_addr;
943 i->addrs.bad_mask[0] = FALSE;
944 i->flags.fromhosts = 1;
945 ipcacheAddEntry(i);
946 ipcacheLockEntry(i);
947 return 0;
948 }
949
950 #ifdef SQUID_SNMP
951 /*
952 * The function to return the ip cache statistics to via SNMP
953 */
954
955 variable_list *
956 snmp_netIpFn(variable_list * Var, snint * ErrP)
957 {
958 variable_list *Answer = NULL;
959 debug(49, 5) ("snmp_netIpFn: Processing request:\n");
960 snmpDebugOid(5, Var->name, Var->name_length);
961 *ErrP = SNMP_ERR_NOERROR;
962
963 switch (Var->name[LEN_SQ_NET + 1]) {
964
965 case IP_ENT:
966 Answer = snmp_var_new_integer(Var->name, Var->name_length,
967 memInUse(MEM_IPCACHE_ENTRY),
968 SMI_GAUGE32);
969 break;
970
971 case IP_REQ:
972 Answer = snmp_var_new_integer(Var->name, Var->name_length,
973 IpcacheStats.requests,
974 SMI_COUNTER32);
975 break;
976
977 case IP_HITS:
978 Answer = snmp_var_new_integer(Var->name, Var->name_length,
979 IpcacheStats.hits,
980 SMI_COUNTER32);
981 break;
982
983 case IP_PENDHIT:
984 Answer = snmp_var_new_integer(Var->name, Var->name_length,
985 0,
986 SMI_GAUGE32);
987 break;
988
989 case IP_NEGHIT:
990 Answer = snmp_var_new_integer(Var->name, Var->name_length,
991 IpcacheStats.negative_hits,
992 SMI_COUNTER32);
993 break;
994
995 case IP_MISS:
996 Answer = snmp_var_new_integer(Var->name, Var->name_length,
997 IpcacheStats.misses,
998 SMI_COUNTER32);
999 break;
1000
1001 case IP_GHBN:
1002 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1003 0, /* deprecated */
1004 SMI_COUNTER32);
1005 break;
1006
1007 case IP_LOC:
1008 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1009 0, /* deprecated */
1010 SMI_COUNTER32);
1011 break;
1012
1013 default:
1014 *ErrP = SNMP_ERR_NOSUCHNAME;
1015 snmp_var_free(Answer);
1016 return (NULL);
1017 }
1018
1019 return Answer;
1020 }
1021
1022 #endif /*SQUID_SNMP */