]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ipcache.cc
Merged from trunk
[thirdparty/squid.git] / src / ipcache.cc
1 /*
2 * DEBUG: section 14 IP Cache
3 * AUTHOR: Harvest Derived
4 *
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
7 *
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
30 *
31 */
32
33 #include "squid.h"
34 #include "CacheManager.h"
35 #include "cbdata.h"
36 #include "dlink.h"
37 #include "DnsLookupDetails.h"
38 #include "event.h"
39 #include "ip/Address.h"
40 #include "ip/tools.h"
41 #include "ipcache.h"
42 #include "Mem.h"
43 #include "mgr/Registration.h"
44 #include "rfc3596.h"
45 #include "SquidConfig.h"
46 #include "SquidDns.h"
47 #include "SquidTime.h"
48 #include "StatCounters.h"
49 #include "Store.h"
50 #include "wordlist.h"
51
52 #if SQUID_SNMP
53 #include "snmp_core.h"
54 #endif
55
56 /**
57 \defgroup IPCacheAPI IP Cache API
58 \ingroup Components
59 \section Introduction Introduction
60 \par
61 * The IP cache is a built-in component of squid providing
62 * Hostname to IP-Number translation functionality and managing
63 * the involved data-structures. Efficiency concerns require
64 * mechanisms that allow non-blocking access to these mappings.
65 * The IP cache usually doesn't block on a request except for
66 * special cases where this is desired (see below).
67 *
68 \todo IP Cache should have its own API *.h header file.
69 */
70
71 /**
72 \defgroup IPCacheInternal IP Cache Internals
73 \ingroup IPCacheAPI
74 \todo when IP cache is provided as a class. These sub-groups will be obsolete
75 * for now they are used to seperate the public and private functions.
76 * with the private ones all being in IPCachInternal and public in IPCacheAPI
77 *
78 \section InternalOperation Internal Operation
79 *
80 * Internally, the execution flow is as follows: On a miss,
81 * ipcache_getnbhostbyname checks whether a request for
82 * this name is already pending, and if positive, it creates
83 * a new entry using ipcacheAddNew with the IP_PENDING
84 * flag set . Then it calls ipcacheAddPending to add a
85 * request to the queue together with data and handler. Else,
86 * ipcache_dnsDispatch() is called to directly create a
87 * DNS query or to ipcacheEnqueue() if all no DNS port
88 * is free. ipcache_call_pending() is called regularly
89 * to walk down the pending list and call handlers. LRU clean-up
90 * is performed through ipcache_purgelru() according to
91 * the ipcache_high threshold.
92 */
93
94 /**
95 \ingroup IPCacheAPI
96 *
97 * The data structure used for storing name-address mappings
98 * is a small hashtable (static hash_table *ip_table),
99 * where structures of type ipcache_entry whose most
100 * interesting members are:
101 */
102 class ipcache_entry
103 {
104 public:
105 hash_link hash; /* must be first */
106 time_t lastref;
107 time_t expires;
108 ipcache_addrs addrs;
109 IPH *handler;
110 void *handlerData;
111 char *error_message;
112
113 struct timeval request_time;
114 dlink_node lru;
115 unsigned short locks;
116 struct {
117 bool negcached;
118 bool fromhosts;
119 } flags;
120
121 int age() const; ///< time passed since request_time or -1 if unknown
122 };
123
124 /// \ingroup IPCacheInternal
125 static struct _ipcache_stats {
126 int requests;
127 int replies;
128 int hits;
129 int misses;
130 int negative_hits;
131 int numeric_hits;
132 int rr_a;
133 int rr_aaaa;
134 int rr_cname;
135 int cname_only;
136 int invalid;
137 } IpcacheStats;
138
139 /// \ingroup IPCacheInternal
140 static dlink_list lru_list;
141
142 // forward-decls
143 static void stat_ipcache_get(StoreEntry *);
144
145 static FREE ipcacheFreeEntry;
146 static IDNSCB ipcacheHandleReply;
147 static int ipcacheExpiredEntry(ipcache_entry *);
148 static int ipcacheParse(ipcache_entry *, const rfc1035_rr *, int, const char *error);
149 static ipcache_entry *ipcache_get(const char *);
150 static void ipcacheLockEntry(ipcache_entry *);
151 static void ipcacheStatPrint(ipcache_entry *, StoreEntry *);
152 static void ipcacheUnlockEntry(ipcache_entry *);
153 static void ipcacheRelease(ipcache_entry *, bool dofree = true);
154
155 /// \ingroup IPCacheInternal
156 static ipcache_addrs static_addrs;
157 /// \ingroup IPCacheInternal
158 static hash_table *ip_table = NULL;
159
160 /// \ingroup IPCacheInternal
161 static long ipcache_low = 180;
162 /// \ingroup IPCacheInternal
163 static long ipcache_high = 200;
164
165 #if LIBRESOLV_DNS_TTL_HACK
166 extern int _dns_ttl_;
167 #endif
168
169 /// \ingroup IPCacheInternal
170 inline int ipcacheCount() { return ip_table ? ip_table->count : 0; }
171
172 int
173 ipcache_entry::age() const
174 {
175 return request_time.tv_sec ? tvSubMsec(request_time, current_time) : -1;
176 }
177
178 /**
179 \ingroup IPCacheInternal
180 *
181 * removes the given ipcache entry
182 */
183 static void
184 ipcacheRelease(ipcache_entry * i, bool dofree)
185 {
186 if (!i) {
187 debugs(14, DBG_CRITICAL, "ipcacheRelease: Releasing entry with i=<NULL>");
188 return;
189 }
190
191 if (!i || !i->hash.key) {
192 debugs(14, DBG_CRITICAL, "ipcacheRelease: Releasing entry without hash link!");
193 return;
194 }
195
196 debugs(14, 3, "ipcacheRelease: Releasing entry for '" << (const char *) i->hash.key << "'");
197
198 hash_remove_link(ip_table, (hash_link *) i);
199 dlinkDelete(&i->lru, &lru_list);
200 if (dofree)
201 ipcacheFreeEntry(i);
202 }
203
204 /// \ingroup IPCacheInternal
205 static ipcache_entry *
206 ipcache_get(const char *name)
207 {
208 if (ip_table != NULL)
209 return (ipcache_entry *) hash_lookup(ip_table, name);
210 else
211 return NULL;
212 }
213
214 /// \ingroup IPCacheInternal
215 static int
216 ipcacheExpiredEntry(ipcache_entry * i)
217 {
218 /* all static entries are locked, so this takes care of them too */
219
220 if (i->locks != 0)
221 return 0;
222
223 if (i->addrs.count == 0)
224 if (0 == i->flags.negcached)
225 return 1;
226
227 if (i->expires > squid_curtime)
228 return 0;
229
230 return 1;
231 }
232
233 /// \ingroup IPCacheAPI
234 void
235 ipcache_purgelru(void *voidnotused)
236 {
237 dlink_node *m;
238 dlink_node *prev = NULL;
239 ipcache_entry *i;
240 int removed = 0;
241 eventAdd("ipcache_purgelru", ipcache_purgelru, NULL, 10.0, 1);
242
243 for (m = lru_list.tail; m; m = prev) {
244 if (ipcacheCount() < ipcache_low)
245 break;
246
247 prev = m->prev;
248
249 i = (ipcache_entry *)m->data;
250
251 if (i->locks != 0)
252 continue;
253
254 ipcacheRelease(i);
255
256 ++removed;
257 }
258
259 debugs(14, 9, "ipcache_purgelru: removed " << removed << " entries");
260 }
261
262 /**
263 \ingroup IPCacheInternal
264 *
265 * purges entries added from /etc/hosts (or whatever).
266 */
267 static void
268 purge_entries_fromhosts(void)
269 {
270 dlink_node *m = lru_list.head;
271 ipcache_entry *i = NULL, *t;
272
273 while (m) {
274 if (i != NULL) { /* need to delay deletion */
275 ipcacheRelease(i); /* we just override locks */
276 i = NULL;
277 }
278
279 t = (ipcache_entry*)m->data;
280
281 if (t->flags.fromhosts)
282 i = t;
283
284 m = m->next;
285 }
286
287 if (i != NULL)
288 ipcacheRelease(i);
289 }
290
291 /**
292 \ingroup IPCacheInternal
293 *
294 * create blank ipcache_entry
295 */
296 static ipcache_entry *
297 ipcacheCreateEntry(const char *name)
298 {
299 static ipcache_entry *i;
300 i = (ipcache_entry *)memAllocate(MEM_IPCACHE_ENTRY);
301 i->hash.key = xstrdup(name);
302 Tolower(static_cast<char*>(i->hash.key));
303 i->expires = squid_curtime + Config.negativeDnsTtl;
304 return i;
305 }
306
307 /// \ingroup IPCacheInternal
308 static void
309 ipcacheAddEntry(ipcache_entry * i)
310 {
311 hash_link *e = (hash_link *)hash_lookup(ip_table, i->hash.key);
312
313 if (NULL != e) {
314 /* avoid colission */
315 ipcache_entry *q = (ipcache_entry *) e;
316 ipcacheRelease(q);
317 }
318
319 hash_join(ip_table, &i->hash);
320 dlinkAdd(i, &i->lru, &lru_list);
321 i->lastref = squid_curtime;
322 }
323
324 /**
325 \ingroup IPCacheInternal
326 *
327 * walks down the pending list, calling handlers
328 */
329 static void
330 ipcacheCallback(ipcache_entry *i, int wait)
331 {
332 IPH *callback = i->handler;
333 void *cbdata = NULL;
334 i->lastref = squid_curtime;
335
336 if (!i->handler)
337 return;
338
339 ipcacheLockEntry(i);
340
341 callback = i->handler;
342
343 i->handler = NULL;
344
345 if (cbdataReferenceValidDone(i->handlerData, &cbdata)) {
346 const DnsLookupDetails details(i->error_message, wait);
347 callback((i->addrs.count ? &i->addrs : NULL), details, cbdata);
348 }
349
350 ipcacheUnlockEntry(i);
351 }
352
353 /// \ingroup IPCacheAPI
354 static int
355 ipcacheParse(ipcache_entry *i, const rfc1035_rr * answers, int nr, const char *error_message)
356 {
357 int k;
358 int j = 0;
359 int na = 0;
360 int ttl = 0;
361 const char *name = (const char *)i->hash.key;
362 int cname_found = 0;
363
364 i->expires = squid_curtime + Config.negativeDnsTtl;
365 i->flags.negcached = true;
366 safe_free(i->addrs.in_addrs);
367 assert(i->addrs.in_addrs == NULL);
368 safe_free(i->addrs.bad_mask);
369 assert(i->addrs.bad_mask == NULL);
370 safe_free(i->error_message);
371 assert(i->error_message == NULL);
372 i->addrs.count = 0;
373
374 if (nr < 0) {
375 debugs(14, 3, "ipcacheParse: Lookup failed '" << error_message << "' for '" << (const char *)i->hash.key << "'");
376 i->error_message = xstrdup(error_message);
377 return -1;
378 }
379
380 if (nr == 0) {
381 debugs(14, 3, "ipcacheParse: No DNS records in response to '" << name << "'");
382 i->error_message = xstrdup("No DNS records");
383 return -1;
384 }
385
386 debugs(14, 3, "ipcacheParse: " << nr << " answers for '" << name << "'");
387 assert(answers);
388
389 for (k = 0; k < nr; ++k) {
390
391 if (Ip::EnableIpv6 && answers[k].type == RFC1035_TYPE_AAAA) {
392 if (answers[k].rdlength != sizeof(struct in6_addr)) {
393 debugs(14, DBG_IMPORTANT, "ipcacheParse: Invalid IPv6 address in response to '" << name << "'");
394 continue;
395 }
396 ++na;
397 ++IpcacheStats.rr_aaaa;
398 continue;
399 }
400
401 if (answers[k].type == RFC1035_TYPE_A) {
402 if (answers[k].rdlength != sizeof(struct in_addr)) {
403 debugs(14, DBG_IMPORTANT, "ipcacheParse: Invalid IPv4 address in response to '" << name << "'");
404 continue;
405 }
406 ++na;
407 ++IpcacheStats.rr_a;
408 continue;
409 }
410
411 /* With A and AAAA, the CNAME does not necessarily come with additional records to use. */
412 if (answers[k].type == RFC1035_TYPE_CNAME) {
413 cname_found=1;
414 ++IpcacheStats.rr_cname;
415 continue;
416 }
417
418 // otherwise its an unknown RR. debug at level 9 since we usually want to ignore these and they are common.
419 debugs(14, 9, HERE << "Unknown RR type received: type=" << answers[k].type << " starting at " << &(answers[k]) );
420 }
421 if (na == 0) {
422 debugs(14, DBG_IMPORTANT, "ipcacheParse: No Address records in response to '" << name << "'");
423 i->error_message = xstrdup("No Address records");
424 if (cname_found)
425 ++IpcacheStats.cname_only;
426 return 0;
427 }
428
429 i->addrs.in_addrs = static_cast<Ip::Address *>(xcalloc(na, sizeof(Ip::Address)));
430 for (int l = 0; l < na; ++l)
431 i->addrs.in_addrs[l].setEmpty(); // perform same init actions as constructor would.
432 i->addrs.bad_mask = (unsigned char *)xcalloc(na, sizeof(unsigned char));
433
434 for (j = 0, k = 0; k < nr; ++k) {
435
436 if (answers[k].type == RFC1035_TYPE_A) {
437 if (answers[k].rdlength != sizeof(struct in_addr))
438 continue;
439
440 struct in_addr temp;
441 memcpy(&temp, answers[k].rdata, sizeof(struct in_addr));
442 i->addrs.in_addrs[j] = temp;
443
444 debugs(14, 3, "ipcacheParse: " << name << " #" << j << " " << i->addrs.in_addrs[j]);
445 ++j;
446
447 } else if (Ip::EnableIpv6 && answers[k].type == RFC1035_TYPE_AAAA) {
448 if (answers[k].rdlength != sizeof(struct in6_addr))
449 continue;
450
451 struct in6_addr temp;
452 memcpy(&temp, answers[k].rdata, sizeof(struct in6_addr));
453 i->addrs.in_addrs[j] = temp;
454
455 debugs(14, 3, "ipcacheParse: " << name << " #" << j << " " << i->addrs.in_addrs[j] );
456 ++j;
457 }
458 if (ttl == 0 || (int) answers[k].ttl < ttl)
459 ttl = answers[k].ttl;
460 }
461
462 assert(j == na);
463
464 if (na < 256)
465 i->addrs.count = (unsigned char) na;
466 else
467 i->addrs.count = 255;
468
469 if (ttl > Config.positiveDnsTtl)
470 ttl = Config.positiveDnsTtl;
471
472 if (ttl < Config.negativeDnsTtl)
473 ttl = Config.negativeDnsTtl;
474
475 i->expires = squid_curtime + ttl;
476
477 i->flags.negcached = false;
478
479 return i->addrs.count;
480 }
481
482 /// \ingroup IPCacheInternal
483 static void
484 ipcacheHandleReply(void *data, const rfc1035_rr * answers, int na, const char *error_message)
485 {
486 ipcache_entry *i;
487 static_cast<generic_cbdata *>(data)->unwrap(&i);
488 ++IpcacheStats.replies;
489 const int age = i->age();
490 statCounter.dns.svcTime.count(age);
491
492 int done = ipcacheParse(i, answers, na, error_message);
493
494 /* If we have not produced either IPs or Error immediately, wait for recursion to finish. */
495 if (done != 0 || error_message != NULL) {
496 ipcacheAddEntry(i);
497 ipcacheCallback(i, age);
498 }
499 }
500
501 /**
502 \ingroup IPCacheAPI
503 *
504 \param name Host to resolve.
505 \param handler Pointer to the function to be called when the reply
506 * from the IP cache (or the DNS if the IP cache misses)
507 \param handlerData Information that is passed to the handler and does not affect the IP cache.
508 *
509 * XXX: on hits and some errors, the handler is called immediately instead
510 * of scheduling an async call. This reentrant behavior means that the
511 * user job must be extra careful after calling ipcache_nbgethostbyname,
512 * especially if the handler destroys the job. Moreover, the job has
513 * no way of knowing whether the reentrant call happened.
514 * Comm::Connection setup usually protects the job by scheduling an async call,
515 * but some user code calls ipcache_nbgethostbyname directly.
516 */
517 void
518 ipcache_nbgethostbyname(const char *name, IPH * handler, void *handlerData)
519 {
520 ipcache_entry *i = NULL;
521 const ipcache_addrs *addrs = NULL;
522 generic_cbdata *c;
523 debugs(14, 4, "ipcache_nbgethostbyname: Name '" << name << "'.");
524 ++IpcacheStats.requests;
525
526 if (name == NULL || name[0] == '\0') {
527 debugs(14, 4, "ipcache_nbgethostbyname: Invalid name!");
528 ++IpcacheStats.invalid;
529 const DnsLookupDetails details("Invalid hostname", -1); // error, no lookup
530 if (handler)
531 handler(NULL, details, handlerData);
532 return;
533 }
534
535 if ((addrs = ipcacheCheckNumeric(name))) {
536 debugs(14, 4, "ipcache_nbgethostbyname: BYPASS for '" << name << "' (already numeric)");
537 ++IpcacheStats.numeric_hits;
538 const DnsLookupDetails details(NULL, -1); // no error, no lookup
539 if (handler)
540 handler(addrs, details, handlerData);
541 return;
542 }
543
544 i = ipcache_get(name);
545
546 if (NULL == i) {
547 /* miss */
548 (void) 0;
549 } else if (ipcacheExpiredEntry(i)) {
550 /* hit, but expired -- bummer */
551 ipcacheRelease(i);
552 i = NULL;
553 } else {
554 /* hit */
555 debugs(14, 4, "ipcache_nbgethostbyname: HIT for '" << name << "'");
556
557 if (i->flags.negcached)
558 ++IpcacheStats.negative_hits;
559 else
560 ++IpcacheStats.hits;
561
562 i->handler = handler;
563
564 i->handlerData = cbdataReference(handlerData);
565
566 ipcacheCallback(i, -1); // no lookup
567
568 return;
569 }
570
571 debugs(14, 5, "ipcache_nbgethostbyname: MISS for '" << name << "'");
572 ++IpcacheStats.misses;
573 i = ipcacheCreateEntry(name);
574 i->handler = handler;
575 i->handlerData = cbdataReference(handlerData);
576 i->request_time = current_time;
577 c = new generic_cbdata(i);
578 idnsALookup(hashKeyStr(&i->hash), ipcacheHandleReply, c);
579 }
580
581 /// \ingroup IPCacheInternal
582 static void
583 ipcacheRegisterWithCacheManager(void)
584 {
585 Mgr::RegisterAction("ipcache",
586 "IP Cache Stats and Contents",
587 stat_ipcache_get, 0, 1);
588 }
589
590 /**
591 \ingroup IPCacheAPI
592 *
593 * Initialize the ipcache.
594 * Is called from mainInitialize() after disk initialization
595 * and prior to the reverse FQDNCache initialization
596 */
597 void
598 ipcache_init(void)
599 {
600 int n;
601 debugs(14, DBG_IMPORTANT, "Initializing IP Cache...");
602 memset(&IpcacheStats, '\0', sizeof(IpcacheStats));
603 memset(&lru_list, '\0', sizeof(lru_list));
604 memset(&static_addrs, '\0', sizeof(ipcache_addrs));
605
606 static_addrs.in_addrs = static_cast<Ip::Address *>(xcalloc(1, sizeof(Ip::Address)));
607 static_addrs.in_addrs->setEmpty(); // properly setup the Ip::Address!
608 static_addrs.bad_mask = (unsigned char *)xcalloc(1, sizeof(unsigned char));
609 ipcache_high = (long) (((float) Config.ipcache.size *
610 (float) Config.ipcache.high) / (float) 100);
611 ipcache_low = (long) (((float) Config.ipcache.size *
612 (float) Config.ipcache.low) / (float) 100);
613 n = hashPrime(ipcache_high / 4);
614 ip_table = hash_create((HASHCMP *) strcmp, n, hash4);
615 memDataInit(MEM_IPCACHE_ENTRY, "ipcache_entry", sizeof(ipcache_entry), 0);
616
617 ipcacheRegisterWithCacheManager();
618 }
619
620 /**
621 \ingroup IPCacheAPI
622 *
623 * Is different from ipcache_nbgethostbyname in that it only checks
624 * if an entry exists in the cache and does not by default contact the DNS,
625 * unless this is requested, by setting the flags.
626 *
627 \param name Host name to resolve.
628 \param flags Default is NULL, set to IP_LOOKUP_IF_MISS
629 * to explicitly perform DNS lookups.
630 *
631 \retval NULL An error occured during lookup
632 \retval NULL No results available in cache and no lookup specified
633 \retval * Pointer to the ipcahce_addrs structure containing the lookup results
634 */
635 const ipcache_addrs *
636 ipcache_gethostbyname(const char *name, int flags)
637 {
638 ipcache_entry *i = NULL;
639 ipcache_addrs *addrs;
640 assert(name);
641 debugs(14, 3, "ipcache_gethostbyname: '" << name << "', flags=" << std::hex << flags);
642 ++IpcacheStats.requests;
643 i = ipcache_get(name);
644
645 if (NULL == i) {
646 (void) 0;
647 } else if (ipcacheExpiredEntry(i)) {
648 ipcacheRelease(i);
649 i = NULL;
650 } else if (i->flags.negcached) {
651 ++IpcacheStats.negative_hits;
652 // ignore i->error_message: the caller just checks IP cache presence
653 return NULL;
654 } else {
655 ++IpcacheStats.hits;
656 i->lastref = squid_curtime;
657 // ignore i->error_message: the caller just checks IP cache presence
658 return &i->addrs;
659 }
660
661 /* no entry [any more] */
662
663 if ((addrs = ipcacheCheckNumeric(name))) {
664 ++IpcacheStats.numeric_hits;
665 return addrs;
666 }
667
668 ++IpcacheStats.misses;
669
670 if (flags & IP_LOOKUP_IF_MISS)
671 ipcache_nbgethostbyname(name, NULL, NULL);
672
673 return NULL;
674 }
675
676 /// \ingroup IPCacheInternal
677 static void
678 ipcacheStatPrint(ipcache_entry * i, StoreEntry * sentry)
679 {
680 int k;
681 char buf[MAX_IPSTRLEN];
682
683 if (!sentry) {
684 debugs(14, DBG_CRITICAL, HERE << "CRITICAL: sentry is NULL!");
685 return;
686 }
687
688 if (!i) {
689 debugs(14, DBG_CRITICAL, HERE << "CRITICAL: ipcache_entry is NULL!");
690 storeAppendPrintf(sentry, "CRITICAL ERROR\n");
691 return;
692 }
693
694 int count = i->addrs.count;
695
696 storeAppendPrintf(sentry, " %-32.32s %c%c %6d %6d %2d(%2d)",
697 hashKeyStr(&i->hash),
698 i->flags.fromhosts ? 'H' : ' ',
699 i->flags.negcached ? 'N' : ' ',
700 (int) (squid_curtime - i->lastref),
701 (int) ((i->flags.fromhosts ? -1 : i->expires - squid_curtime)),
702 (int) i->addrs.count,
703 (int) i->addrs.badcount);
704
705 /** \par
706 * Negative-cached entries have no IPs listed. */
707 if (i->flags.negcached) {
708 storeAppendPrintf(sentry, "\n");
709 return;
710 }
711
712 /** \par
713 * Cached entries have IPs listed with a BNF of: ip-address '-' ('OK'|'BAD') */
714 for (k = 0; k < count; ++k) {
715 /* Display tidy-up: IPv6 are so big make the list vertical */
716 if (k == 0)
717 storeAppendPrintf(sentry, " %45.45s-%3s\n",
718 i->addrs.in_addrs[k].toStr(buf,MAX_IPSTRLEN),
719 i->addrs.bad_mask[k] ? "BAD" : "OK ");
720 else
721 storeAppendPrintf(sentry, "%s %45.45s-%3s\n",
722 " ", /* blank-space indenting IP list */
723 i->addrs.in_addrs[k].toStr(buf,MAX_IPSTRLEN),
724 i->addrs.bad_mask[k] ? "BAD" : "OK ");
725 }
726 }
727
728 /**
729 \ingroup IPCacheInternal
730 *
731 * process objects list
732 */
733 void
734 stat_ipcache_get(StoreEntry * sentry)
735 {
736 dlink_node *m;
737 assert(ip_table != NULL);
738 storeAppendPrintf(sentry, "IP Cache Statistics:\n");
739 storeAppendPrintf(sentry, "IPcache Entries In Use: %d\n",
740 memInUse(MEM_IPCACHE_ENTRY));
741 storeAppendPrintf(sentry, "IPcache Entries Cached: %d\n",
742 ipcacheCount());
743 storeAppendPrintf(sentry, "IPcache Requests: %d\n",
744 IpcacheStats.requests);
745 storeAppendPrintf(sentry, "IPcache Hits: %d\n",
746 IpcacheStats.hits);
747 storeAppendPrintf(sentry, "IPcache Negative Hits: %d\n",
748 IpcacheStats.negative_hits);
749 storeAppendPrintf(sentry, "IPcache Numeric Hits: %d\n",
750 IpcacheStats.numeric_hits);
751 storeAppendPrintf(sentry, "IPcache Misses: %d\n",
752 IpcacheStats.misses);
753 storeAppendPrintf(sentry, "IPcache Retrieved A: %d\n",
754 IpcacheStats.rr_a);
755 storeAppendPrintf(sentry, "IPcache Retrieved AAAA: %d\n",
756 IpcacheStats.rr_aaaa);
757 storeAppendPrintf(sentry, "IPcache Retrieved CNAME: %d\n",
758 IpcacheStats.rr_cname);
759 storeAppendPrintf(sentry, "IPcache CNAME-Only Response: %d\n",
760 IpcacheStats.cname_only);
761 storeAppendPrintf(sentry, "IPcache Invalid Request: %d\n",
762 IpcacheStats.invalid);
763 storeAppendPrintf(sentry, "\n\n");
764 storeAppendPrintf(sentry, "IP Cache Contents:\n\n");
765 storeAppendPrintf(sentry, " %-31.31s %3s %6s %6s %4s\n",
766 "Hostname",
767 "Flg",
768 "lstref",
769 "TTL",
770 "N(b)");
771
772 for (m = lru_list.head; m; m = m->next) {
773 assert( m->next != m );
774 ipcacheStatPrint((ipcache_entry *)m->data, sentry);
775 }
776 }
777
778 /// \ingroup IPCacheAPI
779 void
780 ipcacheInvalidate(const char *name)
781 {
782 ipcache_entry *i;
783
784 if ((i = ipcache_get(name)) == NULL)
785 return;
786
787 i->expires = squid_curtime;
788
789 /*
790 * NOTE, don't call ipcacheRelease here because we might be here due
791 * to a thread started from a callback.
792 */
793 }
794
795 /// \ingroup IPCacheAPI
796 void
797 ipcacheInvalidateNegative(const char *name)
798 {
799 ipcache_entry *i;
800
801 if ((i = ipcache_get(name)) == NULL)
802 return;
803
804 if (i->flags.negcached)
805 i->expires = squid_curtime;
806
807 /*
808 * NOTE, don't call ipcacheRelease here because we might be here due
809 * to a thread started from a callback.
810 */
811 }
812
813 /// \ingroup IPCacheAPI
814 ipcache_addrs *
815 ipcacheCheckNumeric(const char *name)
816 {
817 Ip::Address ip;
818 /* check if it's already a IP address in text form. */
819
820 /* it may be IPv6-wrapped */
821 if (name[0] == '[') {
822 char *tmp = xstrdup(&name[1]);
823 tmp[strlen(tmp)-1] = '\0';
824 if (!(ip = tmp)) {
825 delete tmp;
826 return NULL;
827 }
828 delete tmp;
829 } else if (!(ip = name))
830 return NULL;
831
832 debugs(14, 4, "ipcacheCheckNumeric: HIT_BYPASS for '" << name << "' == " << ip );
833
834 static_addrs.count = 1;
835
836 static_addrs.cur = 0;
837
838 static_addrs.in_addrs[0] = ip;
839
840 static_addrs.bad_mask[0] = FALSE;
841
842 static_addrs.badcount = 0;
843
844 return &static_addrs;
845 }
846
847 /// \ingroup IPCacheInternal
848 static void
849 ipcacheLockEntry(ipcache_entry * i)
850 {
851 if (i->locks++ == 0) {
852 dlinkDelete(&i->lru, &lru_list);
853 dlinkAdd(i, &i->lru, &lru_list);
854 }
855 }
856
857 /// \ingroup IPCacheInternal
858 static void
859 ipcacheUnlockEntry(ipcache_entry * i)
860 {
861 if (i->locks < 1) {
862 debugs(14, DBG_IMPORTANT, "WARNING: ipcacheEntry unlocked with no lock! locks=" << i->locks);
863 return;
864 }
865
866 -- i->locks;
867
868 if (ipcacheExpiredEntry(i))
869 ipcacheRelease(i);
870 }
871
872 /// \ingroup IPCacheAPI
873 void
874 ipcacheCycleAddr(const char *name, ipcache_addrs * ia)
875 {
876 ipcache_entry *i;
877 unsigned char k;
878 assert(name || ia);
879
880 if (NULL == ia) {
881 if ((i = ipcache_get(name)) == NULL)
882 return;
883
884 if (i->flags.negcached)
885 return;
886
887 ia = &i->addrs;
888 }
889
890 for (k = 0; k < ia->count; ++k) {
891 if (++ia->cur == ia->count)
892 ia->cur = 0;
893
894 if (!ia->bad_mask[ia->cur])
895 break;
896 }
897
898 if (k == ia->count) {
899 /* All bad, reset to All good */
900 debugs(14, 3, "ipcacheCycleAddr: Changing ALL " << name << " addrs from BAD to OK");
901
902 for (k = 0; k < ia->count; ++k)
903 ia->bad_mask[k] = 0;
904
905 ia->badcount = 0;
906
907 ia->cur = 0;
908 }
909
910 /* NP: zero-based so we increase the human-readable number of our position */
911 debugs(14, 3, "ipcacheCycleAddr: " << name << " now at " << ia->in_addrs[ia->cur] << " (" << (ia->cur+1) << " of " << ia->count << ")");
912 }
913
914 /**
915 \ingroup IPCacheAPI
916 *
917 \param name domain name to have an IP marked bad
918 \param addr specific addres to be marked bad
919 */
920 void
921 ipcacheMarkBadAddr(const char *name, const Ip::Address &addr)
922 {
923 ipcache_entry *i;
924 ipcache_addrs *ia;
925 int k;
926
927 /** Does nothing if the domain name does not exist. */
928 if ((i = ipcache_get(name)) == NULL)
929 return;
930
931 ia = &i->addrs;
932
933 for (k = 0; k < (int) ia->count; ++k) {
934 if (addr == ia->in_addrs[k] )
935 break;
936 }
937
938 /** Does nothing if the IP does not exist for the doamin. */
939 if (k == (int) ia->count)
940 return;
941
942 /** Marks the given address as BAD */
943 if (!ia->bad_mask[k]) {
944 ia->bad_mask[k] = TRUE;
945 ++ia->badcount;
946 i->expires = min(squid_curtime + max((time_t)60, Config.negativeDnsTtl), i->expires);
947 debugs(14, 2, "ipcacheMarkBadAddr: " << name << " " << addr );
948 }
949
950 /** then calls ipcacheCycleAddr() to advance the current pointer to the next OK address. */
951 ipcacheCycleAddr(name, ia);
952 }
953
954 /// \ingroup IPCacheAPI
955 void
956 ipcacheMarkAllGood(const char *name)
957 {
958 ipcache_entry *i;
959 ipcache_addrs *ia;
960 int k;
961
962 if ((i = ipcache_get(name)) == NULL)
963 return;
964
965 ia = &i->addrs;
966
967 /* All bad, reset to All good */
968 debugs(14, 3, "ipcacheMarkAllGood: Changing ALL " << name << " addrs to OK (" << ia->badcount << "/" << ia->count << " bad)");
969
970 for (k = 0; k < ia->count; ++k)
971 ia->bad_mask[k] = 0;
972
973 ia->badcount = 0;
974 }
975
976 /// \ingroup IPCacheAPI
977 void
978 ipcacheMarkGoodAddr(const char *name, const Ip::Address &addr)
979 {
980 ipcache_entry *i;
981 ipcache_addrs *ia;
982 int k;
983
984 if ((i = ipcache_get(name)) == NULL)
985 return;
986
987 ia = &i->addrs;
988
989 for (k = 0; k < (int) ia->count; ++k) {
990 if (addr == ia->in_addrs[k])
991 break;
992 }
993
994 if (k == (int) ia->count) /* not found */
995 return;
996
997 if (!ia->bad_mask[k]) /* already OK */
998 return;
999
1000 ia->bad_mask[k] = FALSE;
1001
1002 -- ia->badcount;
1003
1004 debugs(14, 2, "ipcacheMarkGoodAddr: " << name << " " << addr );
1005 }
1006
1007 /// \ingroup IPCacheInternal
1008 static void
1009 ipcacheFreeEntry(void *data)
1010 {
1011 ipcache_entry *i = (ipcache_entry *)data;
1012 safe_free(i->addrs.in_addrs);
1013 safe_free(i->addrs.bad_mask);
1014 safe_free(i->hash.key);
1015 safe_free(i->error_message);
1016 memFree(i, MEM_IPCACHE_ENTRY);
1017 }
1018
1019 /// \ingroup IPCacheAPI
1020 void
1021 ipcacheFreeMemory(void)
1022 {
1023 hashFreeItems(ip_table, ipcacheFreeEntry);
1024 hashFreeMemory(ip_table);
1025 ip_table = NULL;
1026 }
1027
1028 /**
1029 \ingroup IPCacheAPI
1030 *
1031 * Recalculate IP cache size upon reconfigure.
1032 * Is called to clear the IPCache's data structures,
1033 * cancel all pending requests.
1034 */
1035 void
1036 ipcache_restart(void)
1037 {
1038 ipcache_high = (long) (((float) Config.ipcache.size *
1039 (float) Config.ipcache.high) / (float) 100);
1040 ipcache_low = (long) (((float) Config.ipcache.size *
1041 (float) Config.ipcache.low) / (float) 100);
1042 purge_entries_fromhosts();
1043 }
1044
1045 /**
1046 \ingroup IPCacheAPI
1047 *
1048 * Adds a "static" entry from /etc/hosts
1049 *
1050 \param name Hostname to be linked with IP
1051 \param ipaddr IP Address to be cached.
1052 *
1053 \retval 0 Success.
1054 \retval 1 IP address is invalid or other error.
1055 */
1056 int
1057 ipcacheAddEntryFromHosts(const char *name, const char *ipaddr)
1058 {
1059 ipcache_entry *i;
1060
1061 Ip::Address ip;
1062
1063 if (!(ip = ipaddr)) {
1064 if (strchr(ipaddr, ':') && strspn(ipaddr, "0123456789abcdefABCDEF:") == strlen(ipaddr)) {
1065 debugs(14, 3, "ipcacheAddEntryFromHosts: Skipping IPv6 address '" << ipaddr << "'");
1066 } else {
1067 debugs(14, DBG_IMPORTANT, "ipcacheAddEntryFromHosts: Bad IP address '" << ipaddr << "'");
1068 }
1069
1070 return 1;
1071 }
1072
1073 if ((i = ipcache_get(name))) {
1074 if (1 == i->flags.fromhosts) {
1075 ipcacheUnlockEntry(i);
1076 } else if (i->locks > 0) {
1077 debugs(14, DBG_IMPORTANT, "ipcacheAddEntryFromHosts: can't add static entry for locked name '" << name << "'");
1078 return 1;
1079 } else {
1080 ipcacheRelease(i);
1081 }
1082 }
1083
1084 i = ipcacheCreateEntry(name);
1085 i->addrs.count = 1;
1086 i->addrs.cur = 0;
1087 i->addrs.badcount = 0;
1088
1089 i->addrs.in_addrs = static_cast<Ip::Address *>(xcalloc(1, sizeof(Ip::Address)));
1090 i->addrs.bad_mask = (unsigned char *)xcalloc(1, sizeof(unsigned char));
1091 i->addrs.in_addrs[0] = ip;
1092 i->addrs.bad_mask[0] = FALSE;
1093 i->flags.fromhosts = true;
1094 ipcacheAddEntry(i);
1095 ipcacheLockEntry(i);
1096 return 0;
1097 }
1098
1099 #if SQUID_SNMP
1100 /**
1101 \ingroup IPCacheAPI
1102 *
1103 * The function to return the ip cache statistics to via SNMP
1104 */
1105 variable_list *
1106 snmp_netIpFn(variable_list * Var, snint * ErrP)
1107 {
1108 variable_list *Answer = NULL;
1109 MemBuf tmp;
1110 debugs(49, 5, "snmp_netIpFn: Processing request:" << snmpDebugOid(Var->name, Var->name_length, tmp));
1111 *ErrP = SNMP_ERR_NOERROR;
1112
1113 switch (Var->name[LEN_SQ_NET + 1]) {
1114
1115 case IP_ENT:
1116 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1117 ipcacheCount(),
1118 SMI_GAUGE32);
1119 break;
1120
1121 case IP_REQ:
1122 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1123 IpcacheStats.requests,
1124 SMI_COUNTER32);
1125 break;
1126
1127 case IP_HITS:
1128 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1129 IpcacheStats.hits,
1130 SMI_COUNTER32);
1131 break;
1132
1133 case IP_PENDHIT:
1134 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1135 0,
1136 SMI_GAUGE32);
1137 break;
1138
1139 case IP_NEGHIT:
1140 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1141 IpcacheStats.negative_hits,
1142 SMI_COUNTER32);
1143 break;
1144
1145 case IP_MISS:
1146 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1147 IpcacheStats.misses,
1148 SMI_COUNTER32);
1149 break;
1150
1151 case IP_GHBN:
1152 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1153 0, /* deprecated */
1154 SMI_COUNTER32);
1155 break;
1156
1157 case IP_LOC:
1158 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1159 0, /* deprecated */
1160 SMI_COUNTER32);
1161 break;
1162
1163 default:
1164 *ErrP = SNMP_ERR_NOSUCHNAME;
1165 snmp_var_free(Answer);
1166 return (NULL);
1167 }
1168
1169 return Answer;
1170 }
1171
1172 #endif /*SQUID_SNMP */