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