]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ipcache.cc
Merged from trunk
[thirdparty/squid.git] / src / ipcache.cc
1
2 /*
3 * $Id: ipcache.cc,v 1.269 2008/02/26 21:49:35 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 #include "IPAddress.h"
44
45 /**
46 \defgroup IPCacheAPI IP Cache API
47 \ingroup Components
48 \section Introduction Introduction
49 \par
50 * The IP cache is a built-in component of squid providing
51 * Hostname to IP-Number translation functionality and managing
52 * the involved data-structures. Efficiency concerns require
53 * mechanisms that allow non-blocking access to these mappings.
54 * The IP cache usually doesn't block on a request except for
55 * special cases where this is desired (see below).
56 *
57 \todo IP Cache should have its own API *.h header file.
58 */
59
60 /**
61 \defgroup IPCacheInternal IP Cache Internals
62 \ingroup IPCacheAPI
63 \todo when IP cache is provided as a class. These sub-groups will be obsolete
64 * for now they are used to seperate the public and private functions.
65 * with the private ones all being in IPCachInternal and public in IPCacheAPI
66 *
67 \section InternalOperation Internal Operation
68 *
69 * Internally, the execution flow is as follows: On a miss,
70 * ipcache_getnbhostbyname checks whether a request for
71 * this name is already pending, and if positive, it creates
72 * a new entry using ipcacheAddNew with the IP_PENDING
73 * flag set . Then it calls ipcacheAddPending to add a
74 * request to the queue together with data and handler. Else,
75 * ipcache_dnsDispatch() is called to directly create a
76 * DNS query or to ipcacheEnqueue() if all no DNS port
77 * is free. ipcache_call_pending() is called regularly
78 * to walk down the pending list and call handlers. LRU clean-up
79 * is performed through ipcache_purgelru() according to
80 * the ipcache_high threshold.
81 */
82
83 /// \ingroup IPCacheAPI
84 typedef struct _ipcache_entry ipcache_entry;
85
86 /**
87 \ingroup IPCacheAPI
88 *
89 * The data structure used for storing name-address mappings
90 * is a small hashtable (static hash_table *ip_table),
91 * where structures of type ipcache_entry whose most
92 * interesting members are:
93 */
94 struct _ipcache_entry
95 {
96 hash_link hash; /* must be first */
97 time_t lastref;
98 time_t expires;
99 ipcache_addrs addrs;
100 IPH *handler;
101 void *handlerData;
102 char *error_message;
103
104 struct timeval request_time;
105 dlink_node lru;
106 unsigned short locks;
107 #if DNS_CNAME
108 unsigned short cname_wait;
109 #endif
110
111 struct
112 {
113 unsigned int negcached:1;
114 unsigned int fromhosts:1;
115 } flags;
116 };
117
118 /// \ingroup IPCacheInternal
119 static struct _ipcache_stats
120 {
121 int requests;
122 int replies;
123 int hits;
124 int misses;
125 int negative_hits;
126 int numeric_hits;
127 int rr_a;
128 int rr_aaaa;
129 int rr_cname;
130 int cname_only;
131 int invalid;
132 } IpcacheStats;
133
134 /// \ingroup IPCacheInternal
135 static dlink_list lru_list;
136
137 static FREE ipcacheFreeEntry;
138 #if USE_DNSSERVERS
139 static HLPCB ipcacheHandleReply;
140 #else
141 static IDNSCB ipcacheHandleReply;
142 #endif
143 static IPH ipcacheHandleCnameRecurse;
144 static int ipcacheExpiredEntry(ipcache_entry *);
145 static int ipcache_testname(void);
146 #if USE_DNSSERVERS
147 static int ipcacheParse(ipcache_entry *, const char *buf);
148 #else
149 static int ipcacheParse(ipcache_entry *, rfc1035_rr *, int, const char *error);
150 #endif
151 static ipcache_entry *ipcache_get(const char *);
152 static void ipcacheLockEntry(ipcache_entry *);
153 static void ipcacheStatPrint(ipcache_entry *, StoreEntry *);
154 static void ipcacheUnlockEntry(ipcache_entry *);
155 static void ipcacheRelease(ipcache_entry *, bool dofree = true);
156
157 /// \ingroup IPCacheInternal
158 static ipcache_addrs static_addrs;
159 /// \ingroup IPCacheInternal
160 static hash_table *ip_table = NULL;
161
162 /// \ingroup IPCacheInternal
163 static long ipcache_low = 180;
164 /// \ingroup IPCacheInternal
165 static long ipcache_high = 200;
166
167 #if LIBRESOLV_DNS_TTL_HACK
168 extern int _dns_ttl_;
169 #endif
170
171 /// \ingroup IPCacheInternal
172 static int
173 ipcache_testname(void)
174 {
175 wordlist *w = NULL;
176 debugs(14, 1, "Performing DNS Tests...");
177
178 if ((w = Config.dns_testname_list) == NULL)
179 return 1;
180
181 for (; w; w = w->next) {
182 if (gethostbyname(w->key) != NULL)
183 return 1;
184 }
185
186 return 0;
187 }
188
189 /**
190 \ingroup IPCacheInternal
191 *
192 * removes the given ipcache entry
193 */
194 static void
195 ipcacheRelease(ipcache_entry * i, bool dofree)
196 {
197 if(!i) {
198 debugs(14, 0, "ipcacheRelease: Releasing entry with i=<NULL>");
199 return;
200 }
201
202 if(!i || !i->hash.key) {
203 debugs(14, 0, "ipcacheRelease: Releasing entry without hash link!");
204 return;
205 }
206
207 debugs(14, 3, "ipcacheRelease: Releasing entry for '" << (const char *) i->hash.key << "'");
208
209 hash_remove_link(ip_table, (hash_link *) i);
210 dlinkDelete(&i->lru, &lru_list);
211 if(dofree)
212 ipcacheFreeEntry(i);
213 }
214
215 /// \ingroup IPCacheInternal
216 static ipcache_entry *
217 ipcache_get(const char *name)
218 {
219 if (ip_table != NULL)
220 return (ipcache_entry *) hash_lookup(ip_table, name);
221 else
222 return NULL;
223 }
224
225 /// \ingroup IPCacheInternal
226 static int
227 ipcacheExpiredEntry(ipcache_entry * i)
228 {
229 /* all static entries are locked, so this takes care of them too */
230
231 if (i->locks != 0)
232 return 0;
233
234 if (i->addrs.count == 0)
235 if (0 == i->flags.negcached)
236 return 1;
237
238 if (i->expires > squid_curtime)
239 return 0;
240
241 return 1;
242 }
243
244 /// \ingroup IPCacheAPI
245 void
246 ipcache_purgelru(void *voidnotused)
247 {
248 dlink_node *m;
249 dlink_node *prev = NULL;
250 ipcache_entry *i;
251 int removed = 0;
252 eventAdd("ipcache_purgelru", ipcache_purgelru, NULL, 10.0, 1);
253
254 for (m = lru_list.tail; m; m = prev) {
255 if (memInUse(MEM_IPCACHE_ENTRY) < ipcache_low)
256 break;
257
258 prev = m->prev;
259
260 i = (ipcache_entry *)m->data;
261
262 if (i->locks != 0)
263 continue;
264
265 ipcacheRelease(i);
266
267 removed++;
268 }
269
270 debugs(14, 9, "ipcache_purgelru: removed " << removed << " entries");
271 }
272
273 /**
274 \ingroup IPCacheInternal
275 *
276 * purges entries added from /etc/hosts (or whatever).
277 */
278 static void
279 purge_entries_fromhosts(void)
280 {
281 dlink_node *m = lru_list.head;
282 ipcache_entry *i = NULL, *t;
283
284 while (m) {
285 if (i != NULL) { /* need to delay deletion */
286 ipcacheRelease(i); /* we just override locks */
287 i = NULL;
288 }
289
290 t = (ipcache_entry*)m->data;
291
292 if (t->flags.fromhosts)
293 i = t;
294
295 m = m->next;
296 }
297
298 if (i != NULL)
299 ipcacheRelease(i);
300 }
301
302 /**
303 \ingroup IPCacheInternal
304 *
305 * create blank ipcache_entry
306 */
307 static ipcache_entry *
308 ipcacheCreateEntry(const char *name)
309 {
310 static ipcache_entry *i;
311 i = (ipcache_entry *)memAllocate(MEM_IPCACHE_ENTRY);
312 i->hash.key = xstrdup(name);
313 i->expires = squid_curtime + Config.negativeDnsTtl;
314 return i;
315 }
316
317 /// \ingroup IPCacheInternal
318 static void
319 ipcacheAddEntry(ipcache_entry * i)
320 {
321 hash_link *e = (hash_link *)hash_lookup(ip_table, i->hash.key);
322
323 #if DNS_CNAME
324 /* INET6 : should NOT be adding this entry until all CNAME have been received. */
325 assert(i->cname_wait == 0);
326 #endif
327
328 if (NULL != e) {
329 /* avoid colission */
330 ipcache_entry *q = (ipcache_entry *) e;
331 #if DNS_CNAME
332 if(q == i) {
333 /* can occur with Multiple-depth CNAME Recursion if parent returned early with additional */
334 /* just need to drop from the hash without releasing actual memory */
335 ipcacheRelease(q, false);
336 }
337 else
338 #endif
339 ipcacheRelease(q);
340 }
341
342 hash_join(ip_table, &i->hash);
343 dlinkAdd(i, &i->lru, &lru_list);
344 i->lastref = squid_curtime;
345 }
346
347 /**
348 \ingroup IPCacheInternal
349 *
350 * walks down the pending list, calling handlers
351 */
352 static void
353 ipcacheCallback(ipcache_entry * i)
354 {
355 IPH *callback = i->handler;
356 void *cbdata = NULL;
357 i->lastref = squid_curtime;
358
359 if (!i->handler)
360 return;
361
362 ipcacheLockEntry(i);
363
364 callback = i->handler;
365
366 i->handler = NULL;
367
368 if (cbdataReferenceValidDone(i->handlerData, &cbdata)) {
369 dns_error_message = i->error_message;
370 callback(i->addrs.count ? &i->addrs : NULL, cbdata);
371 }
372
373 ipcacheUnlockEntry(i);
374 }
375
376 /// \ingroup IPCacheAPI
377 #if USE_DNSSERVERS
378 static int
379 ipcacheParse(ipcache_entry *i, const char *inbuf)
380 {
381 LOCAL_ARRAY(char, buf, DNS_INBUF_SZ);
382 char *token;
383 int ipcount = 0;
384 int ttl;
385 char *A[32];
386 const char *name = (const char *)i->hash.key;
387 i->expires = squid_curtime + Config.negativeDnsTtl;
388 i->flags.negcached = 1;
389 safe_free(i->addrs.in_addrs);
390 safe_free(i->addrs.bad_mask);
391 safe_free(i->error_message);
392 i->addrs.count = 0;
393
394 if (inbuf == NULL) {
395 debugs(14, 1, "ipcacheParse: Got <NULL> reply");
396 i->error_message = xstrdup("Internal Error");
397 return -1;
398 }
399
400 xstrncpy(buf, inbuf, DNS_INBUF_SZ);
401 debugs(14, 5, "ipcacheParse: parsing: {" << buf << "}");
402 token = strtok(buf, w_space);
403
404 if (NULL == token) {
405 debugs(14, 1, "ipcacheParse: expecting result, got '" << inbuf << "'");
406
407 i->error_message = xstrdup("Internal Error");
408 return -1;
409 }
410
411 if (0 == strcmp(token, "$fail")) {
412 token = strtok(NULL, "\n");
413 assert(NULL != token);
414 i->error_message = xstrdup(token);
415 return 0;
416 }
417
418 if (0 != strcmp(token, "$addr")) {
419 debugs(14, 1, "ipcacheParse: expecting '$addr', got '" << inbuf << "' in response to '" << name << "'");
420
421 i->error_message = xstrdup("Internal Error");
422 return -1;
423 }
424
425 token = strtok(NULL, w_space);
426
427 if (NULL == token) {
428 debugs(14, 1, "ipcacheParse: expecting TTL, got '" << inbuf << "' in response to '" << name << "'");
429
430 i->error_message = xstrdup("Internal Error");
431 return -1;
432 }
433
434 ttl = atoi(token);
435
436 while (NULL != (token = strtok(NULL, w_space))) {
437 A[ipcount] = token;
438
439 if (++ipcount == 32)
440 break;
441 }
442
443 if (ipcount > 0) {
444 int j, k;
445
446 i->addrs.in_addrs = (IPAddress *)xcalloc(ipcount, sizeof(IPAddress));
447 for(int l = 0; l < ipcount; l++)
448 i->addrs.in_addrs[l].SetEmpty(); // perform same init actions as constructor would.
449 i->addrs.bad_mask = (unsigned char *)xcalloc(ipcount, sizeof(unsigned char));
450 memset(i->addrs.bad_mask, 0, sizeof(unsigned char) * ipcount);
451
452 for (j = 0, k = 0; k < ipcount; k++) {
453 if ( i->addrs.in_addrs[j] = A[k] )
454 j++;
455 else
456 debugs(14, 1, "ipcacheParse: Invalid IP address '" << A[k] << "' in response to '" << name << "'");
457 }
458
459 i->addrs.count = (unsigned char) j;
460 }
461
462 if (i->addrs.count <= 0) {
463 debugs(14, 1, "ipcacheParse: No addresses in response to '" << name << "'");
464 return -1;
465 }
466
467 if (ttl == 0 || ttl > Config.positiveDnsTtl)
468 ttl = Config.positiveDnsTtl;
469
470 if (ttl < Config.negativeDnsTtl)
471 ttl = Config.negativeDnsTtl;
472
473 i->expires = squid_curtime + ttl;
474
475 i->flags.negcached = 0;
476
477 return i->addrs.count;
478 }
479
480 #else
481 static int
482 ipcacheParse(ipcache_entry *i, rfc1035_rr * answers, int nr, const char *error_message)
483 {
484 int k;
485 int j = 0;
486 int na = 0;
487 int ttl = 0;
488 const char *name = (const char *)i->hash.key;
489 int cname_found = 0;
490
491 i->expires = squid_curtime + Config.negativeDnsTtl;
492 i->flags.negcached = 1;
493 safe_free(i->addrs.in_addrs);
494 assert(i->addrs.in_addrs == NULL);
495 safe_free(i->addrs.bad_mask);
496 assert(i->addrs.bad_mask == NULL);
497 safe_free(i->error_message);
498 assert(i->error_message == NULL);
499 i->addrs.count = 0;
500
501 if (nr < 0) {
502 debugs(14, 3, "ipcacheParse: Lookup failed '" << error_message << "' for '" << (const char *)i->hash.key << "'");
503 i->error_message = xstrdup(error_message);
504 return -1;
505 }
506
507 if (nr == 0) {
508 debugs(14, 3, "ipcacheParse: No DNS records in response to '" << name << "'");
509 i->error_message = xstrdup("No DNS records");
510 return -1;
511 }
512
513 assert(answers);
514
515 for (k = 0; k < nr; k++) {
516
517 #if USE_IPV6
518 if (answers[k].type == RFC1035_TYPE_AAAA) {
519 if (answers[k].rdlength != sizeof(struct in6_addr)) {
520 debugs(14, 1, "ipcacheParse: Invalid IPv6 address in response to '" << name << "'");
521 continue;
522 }
523 na++;
524 IpcacheStats.rr_aaaa++;
525 continue;
526 }
527 #endif
528
529 if (answers[k].type == RFC1035_TYPE_A) {
530 if (answers[k].rdlength != sizeof(struct in_addr)) {
531 debugs(14, 1, "ipcacheParse: Invalid IPv4 address in response to '" << name << "'");
532 continue;
533 }
534 na++;
535 IpcacheStats.rr_a++;
536 continue;
537 }
538
539 /* With A and AAAA, the CNAME does not necessarily come with additional records to use. */
540 if (answers[k].type == RFC1035_TYPE_CNAME) {
541 cname_found=1;
542 IpcacheStats.rr_cname++;
543
544 #if DNS_CNAME
545 debugs(14, 5, "ipcacheParse: " << name << " CNAME " << answers[k].rdata << " (checking destination: " << i << ").");
546 const ipcache_addrs *res = ipcache_gethostbyname(answers[k].rdata, 0);
547 if(res) {
548 na += res->count;
549 debugs(14, 5, "ipcacheParse: CNAME " << answers[k].rdata << " already has " << res->count << " IPs cached.");
550 }
551 else {
552 /* keep going on this, but flag the fact that we need to wait for a CNAME lookup to finish */
553 debugs(14, 5, "ipcacheParse: CNAME " << answers[k].rdata << " has no IPs! Recursing.");
554 ipcache_nbgethostbyname(answers[k].rdata, ipcacheHandleCnameRecurse, new generic_cbdata(i) );
555 i->cname_wait++;
556 }
557 #endif /* DNS_CNAME */
558
559 continue;
560 }
561
562 // otherwise its an unknown RR. debug at level 9 since we usually want to ignore these and they are common.
563 debugs(14, 9, HERE << "Unknown RR type received: type=" << answers[k].type << " starting at " << &(answers[k]) );
564 }
565
566 #if DNS_CNAME
567 if(na == 0 && i->cname_wait >0 ) {
568 /* don't set any error message (yet). Allow recursion to do its work first. */
569 IpcacheStats.cname_only++;
570 return 0;
571 }
572 #endif /* DNS_CNAME */
573
574 if (na == 0) {
575 debugs(14, 1, "ipcacheParse: No Address records in response to '" << name << "'");
576 i->error_message = xstrdup("No Address records");
577 if(cname_found)
578 IpcacheStats.cname_only++;
579 return 0;
580 }
581
582 i->addrs.in_addrs = (IPAddress *)xcalloc(na, sizeof(IPAddress));
583 for(int l = 0; l < na; l++)
584 i->addrs.in_addrs[l].SetEmpty(); // perform same init actions as constructor would.
585 i->addrs.bad_mask = (unsigned char *)xcalloc(na, sizeof(unsigned char));
586
587 for (j = 0, k = 0; k < nr; k++) {
588
589 if (answers[k].type == RFC1035_TYPE_A) {
590 if (answers[k].rdlength != sizeof(struct in_addr))
591 continue;
592
593 struct in_addr temp;
594 xmemcpy(&temp, answers[k].rdata, sizeof(struct in_addr));
595 i->addrs.in_addrs[j] = temp;
596
597 debugs(14, 3, "ipcacheParse: " << name << " #" << j << " " << i->addrs.in_addrs[j]);
598 j++;
599
600 #if USE_IPV6
601 } else if (answers[k].type == RFC1035_TYPE_AAAA) {
602 if (answers[k].rdlength != sizeof(struct in6_addr))
603 continue;
604
605 struct in6_addr temp;
606 xmemcpy(&temp, answers[k].rdata, sizeof(struct in6_addr));
607 i->addrs.in_addrs[j] = temp;
608
609 debugs(14, 3, "ipcacheParse: " << name << " #" << j << " " << i->addrs.in_addrs[j] );
610 j++;
611 #endif
612 }
613 #if DNS_CNAME
614 else if (answers[k].type == RFC1035_TYPE_CNAME) {
615 debugs(14, 3, "ipcacheParse: " << name << " #x CNAME " << answers[k].rdata);
616 const ipcache_addrs *res = ipcache_gethostbyname(answers[k].rdata, 0);
617 if(res) {
618 /* NP: the results of *that* query need to be integrated in place of the CNAME */
619 /* Ideally we should also integrate the min TTL of the above IPA's into ttl. */
620 for(int l = 0; l < res->count; l++, j++) {
621 i->addrs.in_addrs[j] = res->in_addrs[l];
622 debugs(14, 3, "ipcacheParse: " << name << " #" << j << " " << i->addrs.in_addrs[j] );
623 }
624 }
625 else {
626 debugs(14, 9, "ipcacheParse: " << answers[k].rdata << " (CNAME) waiting on A/AAAA records.");
627 }
628 }
629 #endif /* DNS_CNAME */
630
631 if (ttl == 0 || (int) answers[k].ttl < ttl)
632 ttl = answers[k].ttl;
633 }
634
635 assert(j == na);
636
637 if (na < 256)
638 i->addrs.count = (unsigned char) na;
639 else
640 i->addrs.count = 255;
641
642 if (ttl > Config.positiveDnsTtl)
643 ttl = Config.positiveDnsTtl;
644
645 if (ttl < Config.negativeDnsTtl)
646 ttl = Config.negativeDnsTtl;
647
648 i->expires = squid_curtime + ttl;
649
650 i->flags.negcached = 0;
651
652 #if DNS_CNAME
653 /* SPECIAL CASE: may get here IFF CNAME received with Additional records */
654 /* reurn 0/'wait for further details' value. */
655 /* NP: 'No DNS Results' is a return -1 +msg */
656 if(i->cname_wait)
657 return 0;
658 else
659 #endif /* DNS_CNAME */
660 return i->addrs.count;
661 }
662
663 #endif
664
665 /// \ingroup IPCacheInternal
666 static void
667 #if USE_DNSSERVERS
668 ipcacheHandleReply(void *data, char *reply)
669 #else
670 ipcacheHandleReply(void *data, rfc1035_rr * answers, int na, const char *error_message)
671 #endif
672 {
673 int done;
674 ipcache_entry *i;
675 static_cast<generic_cbdata *>(data)->unwrap(&i);
676 IpcacheStats.replies++;
677 statHistCount(&statCounter.dns.svc_time,
678 tvSubMsec(i->request_time, current_time));
679 #if USE_DNSSERVERS
680
681 done = ipcacheParse(i, reply);
682 #else
683
684 done = ipcacheParse(i, answers, na, error_message);
685
686 /* If we have not produced either IPs or Error immediately, wait for recursion to finish. */
687 if(done != 0 || error_message != NULL)
688 #endif
689
690 {
691 ipcacheAddEntry(i);
692 ipcacheCallback(i);
693 }
694 }
695
696 /**
697 \ingroup IPCacheAPI
698 *
699 \param name Host to resolve.
700 \param handler Pointer to the function to be called when the reply
701 * from the IP cache (or the DNS if the IP cache misses)
702 \param handlerData Information that is passed to the handler and does not affect the IP cache.
703 */
704 void
705 ipcache_nbgethostbyname(const char *name, IPH * handler, void *handlerData)
706 {
707 ipcache_entry *i = NULL;
708 const ipcache_addrs *addrs = NULL;
709 generic_cbdata *c;
710 assert(handler != NULL);
711 debugs(14, 4, "ipcache_nbgethostbyname: Name '" << name << "'.");
712 IpcacheStats.requests++;
713
714 if (name == NULL || name[0] == '\0') {
715 debugs(14, 4, "ipcache_nbgethostbyname: Invalid name!");
716 IpcacheStats.invalid++;
717 dns_error_message = "Invalid hostname";
718 handler(NULL, handlerData);
719 return;
720 }
721
722 if ((addrs = ipcacheCheckNumeric(name))) {
723 debugs(14, 4, "ipcache_nbgethostbyname: BYPASS for '" << name << "' (already numeric)");
724 dns_error_message = NULL;
725 IpcacheStats.numeric_hits++;
726 handler(addrs, handlerData);
727 return;
728 }
729
730 i = ipcache_get(name);
731
732 if (NULL == i) {
733 /* miss */
734 (void) 0;
735 } else if (ipcacheExpiredEntry(i)) {
736 /* hit, but expired -- bummer */
737 ipcacheRelease(i);
738 i = NULL;
739 } else {
740 /* hit */
741 debugs(14, 4, "ipcache_nbgethostbyname: HIT for '" << name << "'");
742
743 if (i->flags.negcached)
744 IpcacheStats.negative_hits++;
745 else
746 IpcacheStats.hits++;
747
748 i->handler = handler;
749
750 i->handlerData = cbdataReference(handlerData);
751
752 ipcacheCallback(i);
753
754 return;
755 }
756
757 debugs(14, 5, "ipcache_nbgethostbyname: MISS for '" << name << "'");
758 IpcacheStats.misses++;
759 i = ipcacheCreateEntry(name);
760 i->handler = handler;
761 i->handlerData = cbdataReference(handlerData);
762 i->request_time = current_time;
763 c = new generic_cbdata(i);
764 #if USE_DNSSERVERS
765
766 dnsSubmit(hashKeyStr(&i->hash), ipcacheHandleReply, c);
767 #else
768
769 idnsALookup(hashKeyStr(&i->hash), ipcacheHandleReply, c);
770 #endif
771 }
772
773 /**
774 \ingroup IPCacheAPI
775 *
776 * Initialize the ipcache.
777 * Is called from mainInitialize() after disk initialization
778 * and prior to the reverse FQDNCache initialization
779 */
780 void
781 ipcache_init(void)
782 {
783 int n;
784 debugs(14, 3, "Initializing IP Cache...");
785 memset(&IpcacheStats, '\0', sizeof(IpcacheStats));
786 memset(&lru_list, '\0', sizeof(lru_list));
787 /* test naming lookup */
788
789 if (!opt_dns_tests) {
790 debugs(14, 4, "ipcache_init: Skipping DNS name lookup tests.");
791 } else if (!ipcache_testname()) {
792 fatal("ipcache_init: DNS name lookup tests failed.");
793 } else {
794 debugs(14, 1, "Successful DNS name lookup tests...");
795 }
796
797 memset(&static_addrs, '\0', sizeof(ipcache_addrs));
798
799 static_addrs.in_addrs = (IPAddress *)xcalloc(1, sizeof(IPAddress));
800 static_addrs.in_addrs->SetEmpty(); // properly setup the IPAddress!
801 static_addrs.bad_mask = (unsigned char *)xcalloc(1, sizeof(unsigned char));
802 ipcache_high = (long) (((float) Config.ipcache.size *
803 (float) Config.ipcache.high) / (float) 100);
804 ipcache_low = (long) (((float) Config.ipcache.size *
805 (float) Config.ipcache.low) / (float) 100);
806 n = hashPrime(ipcache_high / 4);
807 ip_table = hash_create((HASHCMP *) strcmp, n, hash4);
808 memDataInit(MEM_IPCACHE_ENTRY, "ipcache_entry", sizeof(ipcache_entry), 0);
809
810 ipcacheRegisterWithCacheManager();
811 }
812
813 /// \ingroup IPCacheAPI
814 void
815 ipcacheRegisterWithCacheManager(void)
816 {
817 CacheManager::GetInstance()->
818 registerAction("ipcache",
819 "IP Cache Stats and Contents",
820 stat_ipcache_get, 0, 1);
821 }
822
823 /**
824 \ingroup IPCacheAPI
825 *
826 * Is different from ipcache_nbgethostbyname in that it only checks
827 * if an entry exists in the cache and does not by default contact the DNS,
828 * unless this is requested, by setting the flags.
829 *
830 \param name Host name to resolve.
831 \param flags Default is NULL, set to IP_LOOKUP_IF_MISS
832 * to explicitly perform DNS lookups.
833 *
834 \retval NULL An error occured during lookup
835 \retval NULL No results available in cache and no lookup specified
836 \retval * Pointer to the ipcahce_addrs structure containing the lookup results
837 */
838 const ipcache_addrs *
839 ipcache_gethostbyname(const char *name, int flags)
840 {
841 ipcache_entry *i = NULL;
842 ipcache_addrs *addrs;
843 assert(name);
844 debugs(14, 3, "ipcache_gethostbyname: '" << name << "', flags=" << std::hex << flags);
845 IpcacheStats.requests++;
846 i = ipcache_get(name);
847
848 if (NULL == i) {
849 (void) 0;
850 } else if (ipcacheExpiredEntry(i)) {
851 ipcacheRelease(i);
852 i = NULL;
853 } else if (i->flags.negcached) {
854 IpcacheStats.negative_hits++;
855 dns_error_message = i->error_message;
856 return NULL;
857 } else {
858 IpcacheStats.hits++;
859 i->lastref = squid_curtime;
860 dns_error_message = i->error_message;
861 return &i->addrs;
862 }
863
864 dns_error_message = NULL;
865
866 if ((addrs = ipcacheCheckNumeric(name))) {
867 IpcacheStats.numeric_hits++;
868 return addrs;
869 }
870
871 IpcacheStats.misses++;
872
873 if (flags & IP_LOOKUP_IF_MISS)
874 ipcache_nbgethostbyname(name, ipcacheHandleCnameRecurse, NULL);
875
876 return NULL;
877 }
878
879 /// \ingroup IPCacheInternal
880 static void
881 ipcacheStatPrint(ipcache_entry * i, StoreEntry * sentry)
882 {
883 int k;
884 int count = i->addrs.count;
885 char buf[MAX_IPSTRLEN];
886
887 if(!sentry) {
888 debugs(14, 0, HERE << "CRITICAL: sentry is NULL!");
889 }
890
891 if(!i) {
892 debugs(14, 0, HERE << "CRITICAL: ipcache_entry is NULL!");
893 storeAppendPrintf(sentry, "CRITICAL ERROR\n");
894 return;
895 }
896
897 storeAppendPrintf(sentry, " %-32.32s %c%c %6d %6d %2d(%2d)",
898 hashKeyStr(&i->hash),
899 i->flags.fromhosts ? 'H' : ' ',
900 i->flags.negcached ? 'N' : ' ',
901 (int) (squid_curtime - i->lastref),
902 (int) ((i->flags.fromhosts ? -1 : i->expires - squid_curtime)),
903 (int) i->addrs.count,
904 (int) i->addrs.badcount);
905
906 /** \par
907 * Negative-cached entries have no IPs listed. */
908 if(i->flags.negcached) {
909 storeAppendPrintf(sentry, "\n");
910 return;
911 }
912
913 /** \par
914 * Cached entries have IPs listed with a BNF of: <IP> '-' ('OK'|'BAD') */
915 for (k = 0; k < count; k++) {
916 /* Display tidy-up: IPv6 are so big make the list vertical */
917 if(k == 0)
918 storeAppendPrintf(sentry, " %45.45s-%3s\n",
919 i->addrs.in_addrs[k].NtoA(buf,MAX_IPSTRLEN),
920 i->addrs.bad_mask[k] ? "BAD" : "OK ");
921 else
922 storeAppendPrintf(sentry, "%s %45.45s-%3s\n",
923 " ", /* blank-space indenting IP list */
924 i->addrs.in_addrs[k].NtoA(buf,MAX_IPSTRLEN),
925 i->addrs.bad_mask[k] ? "BAD" : "OK ");
926 }
927 }
928
929 /**
930 \ingroup IPCacheInternal
931 *
932 * process objects list
933 */
934 void
935 stat_ipcache_get(StoreEntry * sentry)
936 {
937 dlink_node *m;
938 assert(ip_table != NULL);
939 storeAppendPrintf(sentry, "IP Cache Statistics:\n");
940 storeAppendPrintf(sentry, "IPcache Entries: %d\n",
941 memInUse(MEM_IPCACHE_ENTRY));
942 storeAppendPrintf(sentry, "IPcache Requests: %d\n",
943 IpcacheStats.requests);
944 storeAppendPrintf(sentry, "IPcache Hits: %d\n",
945 IpcacheStats.hits);
946 storeAppendPrintf(sentry, "IPcache Negative Hits: %d\n",
947 IpcacheStats.negative_hits);
948 storeAppendPrintf(sentry, "IPcache Numeric Hits: %d\n",
949 IpcacheStats.numeric_hits);
950 storeAppendPrintf(sentry, "IPcache Misses: %d\n",
951 IpcacheStats.misses);
952 storeAppendPrintf(sentry, "IPcache Retrieved A: %d\n",
953 IpcacheStats.rr_a);
954 storeAppendPrintf(sentry, "IPcache Retrieved AAAA: %d\n",
955 IpcacheStats.rr_aaaa);
956 storeAppendPrintf(sentry, "IPcache Retrieved CNAME: %d\n",
957 IpcacheStats.rr_cname);
958 storeAppendPrintf(sentry, "IPcache CNAME-Only Response: %d\n",
959 IpcacheStats.cname_only);
960 storeAppendPrintf(sentry, "IPcache Invalid Request: %d\n",
961 IpcacheStats.invalid);
962 storeAppendPrintf(sentry, "\n\n");
963 storeAppendPrintf(sentry, "IP Cache Contents:\n\n");
964 storeAppendPrintf(sentry, " %-31.31s %3s %6s %6s %4s\n",
965 "Hostname",
966 "Flg",
967 "lstref",
968 "TTL",
969 "N(b)");
970
971 for (m = lru_list.head; m; m = m->next) {
972 assert( m->next != m );
973 ipcacheStatPrint((ipcache_entry *)m->data, sentry);
974 }
975 }
976
977 #if DNS_CNAME
978 /**
979 * Takes two IPAddress arrays and merges them into a single array
980 * which is allocated dynamically to fit the number of unique addresses
981 *
982 \param aaddrs One list to merge
983 \param alen Size of list aaddrs
984 \param baddrs Other list to merge
985 \param alen Size of list baddrs
986 \param out Combined list of unique addresses (sorted with IPv6 first in IPv6-mode)
987 \param outlen Size of list out
988 */
989 void
990 ipcacheMergeIPLists(const IPAddress *aaddrs, const int alen,
991 const IPAddress *baddrs, const int blen,
992 IPAddress **out, int &outlen )
993 {
994 int fc=0, t=0, c=0;
995
996 IPAddress const *ip4ptrs[255];
997 #if USE_IPV6
998 IPAddress const *ip6ptrs[255];
999 #endif
1000 int num_ip4 = 0;
1001 int num_ip6 = 0;
1002
1003 memset(ip4ptrs, 0, sizeof(IPAddress*)*255);
1004 #if USE_IPV6
1005 memset(ip6ptrs, 0, sizeof(IPAddress*)*255);
1006 #endif
1007
1008 // for each unique address in list A - grab ptr
1009 for(t = 0; t < alen; t++) {
1010 if(aaddrs[t].IsIPv4()) {
1011 // check against IPv4 pruned list
1012 for(c = 0; c <= num_ip4; c++) {
1013 if(ip4ptrs[c] && aaddrs[t] == *(ip4ptrs[c]) ) break; // duplicate.
1014 }
1015 if(c > num_ip4) {
1016 ip4ptrs[num_ip4] = &aaddrs[t];
1017 num_ip4++;
1018 }
1019 }
1020 #if USE_IPV6
1021 else if(aaddrs[t].IsIPv6()) {
1022 debugs(14,8, HERE << "A[" << t << "]=IPv6 " << aaddrs[t]);
1023 // check against IPv6 pruned list
1024 for(c = 0; c <= num_ip6; c++) {
1025 if(ip6ptrs[c] && aaddrs[t] == *ip6ptrs[c]) break; // duplicate.
1026 }
1027 if(c > num_ip6) {
1028 ip6ptrs[num_ip6] = &aaddrs[t];
1029 num_ip6++;
1030 }
1031 }
1032 #endif
1033 }
1034
1035 // for each unique address in list B - grab ptr
1036 for(t = 0; t < blen; t++) {
1037 if(baddrs[t].IsIPv4()) {
1038 // check against IPv4 pruned list
1039 for(c = 0; c <= num_ip4; c++) {
1040 if(ip4ptrs[c] && baddrs[t] == *ip4ptrs[c]) break; // duplicate.
1041 }
1042 if(c > num_ip4) {
1043 ip4ptrs[num_ip4] = &baddrs[t];
1044 num_ip4++;
1045 }
1046 }
1047 #if USE_IPV6
1048 else if(baddrs[t].IsIPv6()) {
1049 // check against IPv6 pruned list
1050 for(c = 0; c <= num_ip6; c++) {
1051 if(ip6ptrs[c] && baddrs[t] == *ip6ptrs[c]) break; // duplicate.
1052 }
1053 if(c > num_ip6) {
1054 ip6ptrs[num_ip6] = &baddrs[t];
1055 num_ip6++;
1056 }
1057 }
1058 #endif
1059 }
1060
1061 fc = num_ip6 + num_ip4;
1062
1063 assert(fc > 0);
1064
1065 debugs(14, 5, "ipcacheMergeIPLists: Merge " << alen << "+" << blen << " into " << fc << " unique IPs.");
1066
1067 // copy the old IPs into the new list buffer.
1068 (*out) = (IPAddress*)xcalloc(fc, sizeof(IPAddress));
1069 outlen=0;
1070
1071 assert(out != NULL);
1072
1073 #if USE_IPV6
1074 /* IPv6 are preferred (tried first) over IPv4 */
1075
1076 for(int l = 0; outlen < num_ip6; l++, outlen++) {
1077 (*out)[outlen] = *ip6ptrs[l];
1078 debugs(14, 5, "ipcacheMergeIPLists: #" << outlen << " " << (*out)[outlen] );
1079 }
1080 #endif /* USE_IPV6 */
1081
1082 for(int l = 0; outlen < num_ip4; l++, outlen++) {
1083 (*out)[outlen] = *ip4ptrs[l];
1084 debugs(14, 5, "ipcacheMergeIPLists: #" << outlen << " " << (*out)[outlen] );
1085 }
1086
1087 assert(outlen == fc); // otherwise something broke badly!
1088 }
1089 #endif /* DNS_CNAME */
1090
1091 /// \ingroup IPCacheInternal
1092 /// Callback.
1093 static void
1094 ipcacheHandleCnameRecurse(const ipcache_addrs *addrs, void *cbdata)
1095 {
1096 #if DNS_CNAME
1097 ipcache_entry *i = NULL;
1098 char *pname = NULL;
1099 IPAddress *tmpbuf = NULL;
1100 int fc = 0;
1101 int ttl = 0;
1102 generic_cbdata* gcb = (generic_cbdata*)cbdata;
1103 // count of addrs at parent and child (REQ as .count is a char type!)
1104 int ccount = 0, pcount = 0;
1105
1106 debugs(14, 5, "ipcacheHandleCnameRecurse: Handling basic A/AAAA response.");
1107
1108 /* IFF no CNAME recursion being processed. do nothing. */
1109 if(cbdata == NULL)
1110 return;
1111
1112 gcb->unwrap(&i);
1113 assert(i != NULL);
1114
1115 // make sure we are actualy waiting for a CNAME callback to be run.
1116 assert(i->cname_wait > 0);
1117 // count this event. its being handled.
1118 i->cname_wait--;
1119
1120 pname = (char*)i->hash.key;
1121 assert(pname != NULL);
1122
1123 debugs(14, 5, "ipcacheHandleCnameRecurse: Handling CNAME recursion. CBDATA('" << gcb->data << "')='" << pname << "' -> " << std::hex << i);
1124
1125 if(i == NULL) {
1126 return; // Parent has expired. Don't merge, just leave for future Ref:
1127 }
1128
1129 /* IFF addrs is NULL (Usually an Error or Timeout occured on lookup.) */
1130 /* Ignore it and HOPE that we got some Additional records to use. */
1131 if(addrs == NULL)
1132 return;
1133
1134 ccount = (0+ addrs->count);
1135 pcount = (0+ i->addrs.count);
1136 ttl = i->expires;
1137
1138 /* IFF no CNAME results. do none of the processing BUT finish anyway. */
1139 if(addrs) {
1140
1141 debugs(14, 5, "ipcacheHandleCnameRecurse: Merge IP Lists for " << pname << " (" << pcount << "+" << ccount << ")");
1142
1143 /* add new IP records to entry */
1144 tmpbuf = i->addrs.in_addrs;
1145 i->addrs.in_addrs = NULL;
1146 ipcacheMergeIPLists(tmpbuf, pcount, addrs->in_addrs, ccount, &(i->addrs.in_addrs), fc);
1147 debugs(14,8, HERE << "in=" << tmpbuf << ", out=" << i->addrs.in_addrs );
1148 assert( (pcount>0 ? tmpbuf!=NULL : tmpbuf==NULL) );
1149 safe_free(tmpbuf);
1150
1151 if( pcount > 0) {
1152 /* IFF the parent initial lookup was given Additional records with A */
1153 // clear the 'bad IP mask'
1154 safe_free(i->addrs.bad_mask);
1155 }
1156 // create a new bad IP mask to fit the new size needed.
1157 if(fc > 0) {
1158 i->addrs.bad_mask = (unsigned char*)xcalloc(fc, sizeof(unsigned char));
1159 memset(i->addrs.bad_mask, 0, sizeof(unsigned char)*fc);
1160 }
1161
1162 if (fc < 256)
1163 i->addrs.count = (unsigned char) fc;
1164 else
1165 i->addrs.count = 255;
1166
1167 if (ttl == 0 || ttl > Config.positiveDnsTtl)
1168 ttl = Config.positiveDnsTtl;
1169
1170 if (ttl < Config.negativeDnsTtl)
1171 ttl = Config.negativeDnsTtl;
1172
1173 i->expires = squid_curtime + ttl;
1174
1175 i->flags.negcached = 0;
1176
1177 i->addrs.cur = 0;
1178
1179 i->addrs.badcount = 0;
1180 }
1181
1182 if(fc == 0) {
1183 i->error_message = xstrdup("No DNS Records");
1184 }
1185
1186 /* finish the lookup we were doing on parent when we got side-tracked for CNAME loop */
1187 if(i->cname_wait == 0) {
1188 ipcacheAddEntry(i);
1189 ipcacheCallback(i);
1190 }
1191 // else still more CNAME to be found.
1192 #endif /* DNS_CNAME */
1193 }
1194
1195 /// \ingroup IPCacheAPI
1196 void
1197 ipcacheInvalidate(const char *name)
1198 {
1199 ipcache_entry *i;
1200
1201 if ((i = ipcache_get(name)) == NULL)
1202 return;
1203
1204 i->expires = squid_curtime;
1205
1206 /*
1207 * NOTE, don't call ipcacheRelease here because we might be here due
1208 * to a thread started from a callback.
1209 */
1210 }
1211
1212 /// \ingroup IPCacheAPI
1213 void
1214 ipcacheInvalidateNegative(const char *name)
1215 {
1216 ipcache_entry *i;
1217
1218 if ((i = ipcache_get(name)) == NULL)
1219 return;
1220
1221 if (i->flags.negcached)
1222 i->expires = squid_curtime;
1223
1224 /*
1225 * NOTE, don't call ipcacheRelease here because we might be here due
1226 * to a thread started from a callback.
1227 */
1228 }
1229
1230 /// \ingroup IPCacheAPI
1231 ipcache_addrs *
1232 ipcacheCheckNumeric(const char *name)
1233 {
1234
1235 IPAddress ip;
1236 /* check if it's already a IP address in text form. */
1237
1238 /* it may be IPv6-wrapped */
1239 if(name[0] == '[') {
1240 char *tmp = xstrdup(&name[1]);
1241 tmp[strlen(tmp)-1] = '\0';
1242 if (!(ip = tmp)) {
1243 delete tmp;
1244 return NULL;
1245 }
1246 delete tmp;
1247 }
1248 else if (!(ip = name))
1249 return NULL;
1250
1251 debugs(14, 4, "ipcacheCheckNumeric: HIT_BYPASS for '" << name << "' == " << ip );
1252
1253 static_addrs.count = 1;
1254
1255 static_addrs.cur = 0;
1256
1257 static_addrs.in_addrs[0] = ip;
1258
1259 static_addrs.bad_mask[0] = FALSE;
1260
1261 static_addrs.badcount = 0;
1262
1263 return &static_addrs;
1264 }
1265
1266 /// \ingroup IPCacheInternal
1267 static void
1268 ipcacheLockEntry(ipcache_entry * i)
1269 {
1270 if (i->locks++ == 0) {
1271 dlinkDelete(&i->lru, &lru_list);
1272 dlinkAdd(i, &i->lru, &lru_list);
1273 }
1274 }
1275
1276 /// \ingroup IPCacheInternal
1277 static void
1278 ipcacheUnlockEntry(ipcache_entry * i)
1279 {
1280 if(i->locks < 1) {
1281 debugs(14, 1, "WARNING: ipcacheEntry unlocked with no lock! locks=" << i->locks);
1282 return;
1283 }
1284
1285 i->locks--;
1286
1287 if (ipcacheExpiredEntry(i))
1288 ipcacheRelease(i);
1289 }
1290
1291 /// \ingroup IPCacheAPI
1292 void
1293 ipcacheCycleAddr(const char *name, ipcache_addrs * ia)
1294 {
1295 ipcache_entry *i;
1296 unsigned char k;
1297 assert(name || ia);
1298
1299 if (NULL == ia) {
1300 if ((i = ipcache_get(name)) == NULL)
1301 return;
1302
1303 if (i->flags.negcached)
1304 return;
1305
1306 ia = &i->addrs;
1307 }
1308
1309 for (k = 0; k < ia->count; k++) {
1310 if (++ia->cur == ia->count)
1311 ia->cur = 0;
1312
1313 if (!ia->bad_mask[ia->cur])
1314 break;
1315 }
1316
1317 if (k == ia->count) {
1318 /* All bad, reset to All good */
1319 debugs(14, 3, "ipcacheCycleAddr: Changing ALL " << name << " addrs from BAD to OK");
1320
1321 for (k = 0; k < ia->count; k++)
1322 ia->bad_mask[k] = 0;
1323
1324 ia->badcount = 0;
1325
1326 ia->cur = 0;
1327 }
1328
1329 debugs(14, 3, "ipcacheCycleAddr: " << name << " now at " << ia->in_addrs[ia->cur] << " (" << ia->cur << " of " << ia->count << ")");
1330 }
1331
1332 /**
1333 \ingroup IPCacheAPI
1334 *
1335 \param name domain name to have an IP marked bad
1336 \param addr specific addres to be marked bad
1337 */
1338 void
1339 ipcacheMarkBadAddr(const char *name, IPAddress &addr)
1340 {
1341 ipcache_entry *i;
1342 ipcache_addrs *ia;
1343 int k;
1344
1345 /** Does nothing if the domain name does not exist. */
1346 if ((i = ipcache_get(name)) == NULL)
1347 return;
1348
1349 ia = &i->addrs;
1350
1351 for (k = 0; k < (int) ia->count; k++)
1352 {
1353 if (addr == ia->in_addrs[k] )
1354 break;
1355 }
1356
1357 /** Does nothing if the IP does not exist for the doamin. */
1358 if (k == (int) ia->count)
1359 return;
1360
1361 /** Marks the given address as BAD */
1362 if (!ia->bad_mask[k])
1363 {
1364 ia->bad_mask[k] = TRUE;
1365 ia->badcount++;
1366 i->expires = XMIN(squid_curtime + XMAX((time_t)60, Config.negativeDnsTtl), i->expires);
1367 debugs(14, 2, "ipcacheMarkBadAddr: " << name << " " << addr );
1368 }
1369
1370 /** then calls ipcacheCycleAddr() to advance the current pointer to the next OK address. */
1371 ipcacheCycleAddr(name, ia);
1372 }
1373
1374 /// \ingroup IPCacheAPI
1375 void
1376 ipcacheMarkGoodAddr(const char *name, IPAddress &addr)
1377 {
1378 ipcache_entry *i;
1379 ipcache_addrs *ia;
1380 int k;
1381
1382 if ((i = ipcache_get(name)) == NULL)
1383 return;
1384
1385 ia = &i->addrs;
1386
1387 for (k = 0; k < (int) ia->count; k++)
1388 {
1389 if (addr == ia->in_addrs[k])
1390 break;
1391 }
1392
1393 if (k == (int) ia->count) /* not found */
1394 return;
1395
1396 if (!ia->bad_mask[k]) /* already OK */
1397 return;
1398
1399 ia->bad_mask[k] = FALSE;
1400
1401 ia->badcount--;
1402
1403 debugs(14, 2, "ipcacheMarkGoodAddr: " << name << " " << addr );
1404 }
1405
1406 /// \ingroup IPCacheInternal
1407 static void
1408 ipcacheFreeEntry(void *data)
1409 {
1410 ipcache_entry *i = (ipcache_entry *)data;
1411 safe_free(i->addrs.in_addrs);
1412 safe_free(i->addrs.bad_mask);
1413 safe_free(i->hash.key);
1414 safe_free(i->error_message);
1415 memFree(i, MEM_IPCACHE_ENTRY);
1416 }
1417
1418 /// \ingroup IPCacheAPI
1419 void
1420 ipcacheFreeMemory(void)
1421 {
1422 hashFreeItems(ip_table, ipcacheFreeEntry);
1423 hashFreeMemory(ip_table);
1424 ip_table = NULL;
1425 }
1426
1427 /**
1428 \ingroup IPCacheAPI
1429 *
1430 * Recalculate IP cache size upon reconfigure.
1431 * Is called to clear the IPCache's data structures,
1432 * cancel all pending requests.
1433 */
1434 void
1435 ipcache_restart(void)
1436 {
1437 ipcache_high = (long) (((float) Config.ipcache.size *
1438 (float) Config.ipcache.high) / (float) 100);
1439 ipcache_low = (long) (((float) Config.ipcache.size *
1440 (float) Config.ipcache.low) / (float) 100);
1441 purge_entries_fromhosts();
1442 }
1443
1444 /**
1445 \ingroup IPCacheAPI
1446 *
1447 * Adds a "static" entry from /etc/hosts
1448 *
1449 \param name Hostname to be linked with IP
1450 \param ipaddr IP Address to be cached.
1451 *
1452 \retval 0 Success.
1453 \retval 1 IP address is invalid or other error.
1454 */
1455 int
1456 ipcacheAddEntryFromHosts(const char *name, const char *ipaddr)
1457 {
1458 ipcache_entry *i;
1459
1460 IPAddress ip;
1461
1462 if (!(ip = ipaddr)) {
1463 #if USE_IPV6
1464 if (strchr(ipaddr, ':') && strspn(ipaddr, "0123456789abcdefABCDEF:") == strlen(ipaddr)) {
1465 debugs(14, 3, "ipcacheAddEntryFromHosts: Skipping IPv6 address '" << ipaddr << "'");
1466 } else {
1467 debugs(14, 1, "ipcacheAddEntryFromHosts: Bad IP address '" << ipaddr << "'");
1468 }
1469 #else
1470 debugs(14, 1, "ipcacheAddEntryFromHosts: Bad IP address '" << ipaddr << "'");
1471 #endif
1472
1473 return 1;
1474 }
1475
1476 if ((i = ipcache_get(name))) {
1477 if (1 == i->flags.fromhosts) {
1478 ipcacheUnlockEntry(i);
1479 } else if (i->locks > 0) {
1480 debugs(14, 1, "ipcacheAddEntryFromHosts: can't add static entry for locked name '" << name << "'");
1481 return 1;
1482 } else {
1483 ipcacheRelease(i);
1484 }
1485 }
1486
1487 i = ipcacheCreateEntry(name);
1488 i->addrs.count = 1;
1489 i->addrs.cur = 0;
1490 i->addrs.badcount = 0;
1491
1492 i->addrs.in_addrs = (IPAddress *)xcalloc(1, sizeof(IPAddress));
1493 i->addrs.bad_mask = (unsigned char *)xcalloc(1, sizeof(unsigned char));
1494 i->addrs.in_addrs[0] = ip;
1495 i->addrs.bad_mask[0] = FALSE;
1496 i->flags.fromhosts = 1;
1497 ipcacheAddEntry(i);
1498 ipcacheLockEntry(i);
1499 return 0;
1500 }
1501
1502 #ifdef SQUID_SNMP
1503 /**
1504 \ingroup IPCacheAPI
1505 *
1506 * The function to return the ip cache statistics to via SNMP
1507 */
1508 variable_list *
1509 snmp_netIpFn(variable_list * Var, snint * ErrP)
1510 {
1511 variable_list *Answer = NULL;
1512 debugs(49, 5, "snmp_netIpFn: Processing request:");
1513 snmpDebugOid(5, Var->name, Var->name_length);
1514 *ErrP = SNMP_ERR_NOERROR;
1515
1516 switch (Var->name[LEN_SQ_NET + 1]) {
1517
1518 case IP_ENT:
1519 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1520 memInUse(MEM_IPCACHE_ENTRY),
1521 SMI_GAUGE32);
1522 break;
1523
1524 case IP_REQ:
1525 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1526 IpcacheStats.requests,
1527 SMI_COUNTER32);
1528 break;
1529
1530 case IP_HITS:
1531 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1532 IpcacheStats.hits,
1533 SMI_COUNTER32);
1534 break;
1535
1536 case IP_PENDHIT:
1537 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1538 0,
1539 SMI_GAUGE32);
1540 break;
1541
1542 case IP_NEGHIT:
1543 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1544 IpcacheStats.negative_hits,
1545 SMI_COUNTER32);
1546 break;
1547
1548 case IP_MISS:
1549 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1550 IpcacheStats.misses,
1551 SMI_COUNTER32);
1552 break;
1553
1554 case IP_GHBN:
1555 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1556 0, /* deprecated */
1557 SMI_COUNTER32);
1558 break;
1559
1560 case IP_LOC:
1561 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1562 0, /* deprecated */
1563 SMI_COUNTER32);
1564 break;
1565
1566 default:
1567 *ErrP = SNMP_ERR_NOSUCHNAME;
1568 snmp_var_free(Answer);
1569 return (NULL);
1570 }
1571
1572 return Answer;
1573 }
1574
1575 #endif /*SQUID_SNMP */