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