]> 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 /// \ingroup IPCacheInternal
774 static void
775 ipcacheRegisterWithCacheManager(void)
776 {
777 CacheManager::GetInstance()->
778 registerAction("ipcache",
779 "IP Cache Stats and Contents",
780 stat_ipcache_get, 0, 1);
781 }
782
783
784 /**
785 \ingroup IPCacheAPI
786 *
787 * Initialize the ipcache.
788 * Is called from mainInitialize() after disk initialization
789 * and prior to the reverse FQDNCache initialization
790 */
791 void
792 ipcache_init(void)
793 {
794 int n;
795 debugs(14, 3, "Initializing IP Cache...");
796 memset(&IpcacheStats, '\0', sizeof(IpcacheStats));
797 memset(&lru_list, '\0', sizeof(lru_list));
798 /* test naming lookup */
799
800 if (!opt_dns_tests) {
801 debugs(14, 4, "ipcache_init: Skipping DNS name lookup tests.");
802 } else if (!ipcache_testname()) {
803 fatal("ipcache_init: DNS name lookup tests failed.");
804 } else {
805 debugs(14, 1, "Successful DNS name lookup tests...");
806 }
807
808 memset(&static_addrs, '\0', sizeof(ipcache_addrs));
809
810 static_addrs.in_addrs = (IPAddress *)xcalloc(1, sizeof(IPAddress));
811 static_addrs.in_addrs->SetEmpty(); // properly setup the IPAddress!
812 static_addrs.bad_mask = (unsigned char *)xcalloc(1, sizeof(unsigned char));
813 ipcache_high = (long) (((float) Config.ipcache.size *
814 (float) Config.ipcache.high) / (float) 100);
815 ipcache_low = (long) (((float) Config.ipcache.size *
816 (float) Config.ipcache.low) / (float) 100);
817 n = hashPrime(ipcache_high / 4);
818 ip_table = hash_create((HASHCMP *) strcmp, n, hash4);
819 memDataInit(MEM_IPCACHE_ENTRY, "ipcache_entry", sizeof(ipcache_entry), 0);
820
821 ipcacheRegisterWithCacheManager();
822 }
823
824 /**
825 \ingroup IPCacheAPI
826 *
827 * Is different from ipcache_nbgethostbyname in that it only checks
828 * if an entry exists in the cache and does not by default contact the DNS,
829 * unless this is requested, by setting the flags.
830 *
831 \param name Host name to resolve.
832 \param flags Default is NULL, set to IP_LOOKUP_IF_MISS
833 * to explicitly perform DNS lookups.
834 *
835 \retval NULL An error occured during lookup
836 \retval NULL No results available in cache and no lookup specified
837 \retval * Pointer to the ipcahce_addrs structure containing the lookup results
838 */
839 const ipcache_addrs *
840 ipcache_gethostbyname(const char *name, int flags)
841 {
842 ipcache_entry *i = NULL;
843 ipcache_addrs *addrs;
844 assert(name);
845 debugs(14, 3, "ipcache_gethostbyname: '" << name << "', flags=" << std::hex << flags);
846 IpcacheStats.requests++;
847 i = ipcache_get(name);
848
849 if (NULL == i) {
850 (void) 0;
851 } else if (ipcacheExpiredEntry(i)) {
852 ipcacheRelease(i);
853 i = NULL;
854 } else if (i->flags.negcached) {
855 IpcacheStats.negative_hits++;
856 dns_error_message = i->error_message;
857 return NULL;
858 } else {
859 IpcacheStats.hits++;
860 i->lastref = squid_curtime;
861 dns_error_message = i->error_message;
862 return &i->addrs;
863 }
864
865 dns_error_message = NULL;
866
867 if ((addrs = ipcacheCheckNumeric(name))) {
868 IpcacheStats.numeric_hits++;
869 return addrs;
870 }
871
872 IpcacheStats.misses++;
873
874 if (flags & IP_LOOKUP_IF_MISS)
875 ipcache_nbgethostbyname(name, ipcacheHandleCnameRecurse, NULL);
876
877 return NULL;
878 }
879
880 /// \ingroup IPCacheInternal
881 static void
882 ipcacheStatPrint(ipcache_entry * i, StoreEntry * sentry)
883 {
884 int k;
885 int count = i->addrs.count;
886 char buf[MAX_IPSTRLEN];
887
888 if(!sentry) {
889 debugs(14, 0, HERE << "CRITICAL: sentry is NULL!");
890 }
891
892 if(!i) {
893 debugs(14, 0, HERE << "CRITICAL: ipcache_entry is NULL!");
894 storeAppendPrintf(sentry, "CRITICAL ERROR\n");
895 return;
896 }
897
898 storeAppendPrintf(sentry, " %-32.32s %c%c %6d %6d %2d(%2d)",
899 hashKeyStr(&i->hash),
900 i->flags.fromhosts ? 'H' : ' ',
901 i->flags.negcached ? 'N' : ' ',
902 (int) (squid_curtime - i->lastref),
903 (int) ((i->flags.fromhosts ? -1 : i->expires - squid_curtime)),
904 (int) i->addrs.count,
905 (int) i->addrs.badcount);
906
907 /** \par
908 * Negative-cached entries have no IPs listed. */
909 if(i->flags.negcached) {
910 storeAppendPrintf(sentry, "\n");
911 return;
912 }
913
914 /** \par
915 * Cached entries have IPs listed with a BNF of: <IP> '-' ('OK'|'BAD') */
916 for (k = 0; k < count; k++) {
917 /* Display tidy-up: IPv6 are so big make the list vertical */
918 if(k == 0)
919 storeAppendPrintf(sentry, " %45.45s-%3s\n",
920 i->addrs.in_addrs[k].NtoA(buf,MAX_IPSTRLEN),
921 i->addrs.bad_mask[k] ? "BAD" : "OK ");
922 else
923 storeAppendPrintf(sentry, "%s %45.45s-%3s\n",
924 " ", /* blank-space indenting IP list */
925 i->addrs.in_addrs[k].NtoA(buf,MAX_IPSTRLEN),
926 i->addrs.bad_mask[k] ? "BAD" : "OK ");
927 }
928 }
929
930 /**
931 \ingroup IPCacheInternal
932 *
933 * process objects list
934 */
935 void
936 stat_ipcache_get(StoreEntry * sentry)
937 {
938 dlink_node *m;
939 assert(ip_table != NULL);
940 storeAppendPrintf(sentry, "IP Cache Statistics:\n");
941 storeAppendPrintf(sentry, "IPcache Entries: %d\n",
942 memInUse(MEM_IPCACHE_ENTRY));
943 storeAppendPrintf(sentry, "IPcache Requests: %d\n",
944 IpcacheStats.requests);
945 storeAppendPrintf(sentry, "IPcache Hits: %d\n",
946 IpcacheStats.hits);
947 storeAppendPrintf(sentry, "IPcache Negative Hits: %d\n",
948 IpcacheStats.negative_hits);
949 storeAppendPrintf(sentry, "IPcache Numeric Hits: %d\n",
950 IpcacheStats.numeric_hits);
951 storeAppendPrintf(sentry, "IPcache Misses: %d\n",
952 IpcacheStats.misses);
953 storeAppendPrintf(sentry, "IPcache Retrieved A: %d\n",
954 IpcacheStats.rr_a);
955 storeAppendPrintf(sentry, "IPcache Retrieved AAAA: %d\n",
956 IpcacheStats.rr_aaaa);
957 storeAppendPrintf(sentry, "IPcache Retrieved CNAME: %d\n",
958 IpcacheStats.rr_cname);
959 storeAppendPrintf(sentry, "IPcache CNAME-Only Response: %d\n",
960 IpcacheStats.cname_only);
961 storeAppendPrintf(sentry, "IPcache Invalid Request: %d\n",
962 IpcacheStats.invalid);
963 storeAppendPrintf(sentry, "\n\n");
964 storeAppendPrintf(sentry, "IP Cache Contents:\n\n");
965 storeAppendPrintf(sentry, " %-31.31s %3s %6s %6s %4s\n",
966 "Hostname",
967 "Flg",
968 "lstref",
969 "TTL",
970 "N(b)");
971
972 for (m = lru_list.head; m; m = m->next) {
973 assert( m->next != m );
974 ipcacheStatPrint((ipcache_entry *)m->data, sentry);
975 }
976 }
977
978 #if DNS_CNAME
979 /**
980 * Takes two IPAddress arrays and merges them into a single array
981 * which is allocated dynamically to fit the number of unique addresses
982 *
983 \param aaddrs One list to merge
984 \param alen Size of list aaddrs
985 \param baddrs Other list to merge
986 \param alen Size of list baddrs
987 \param out Combined list of unique addresses (sorted with IPv6 first in IPv6-mode)
988 \param outlen Size of list out
989 */
990 void
991 ipcacheMergeIPLists(const IPAddress *aaddrs, const int alen,
992 const IPAddress *baddrs, const int blen,
993 IPAddress **out, int &outlen )
994 {
995 int fc=0, t=0, c=0;
996
997 IPAddress const *ip4ptrs[255];
998 #if USE_IPV6
999 IPAddress const *ip6ptrs[255];
1000 #endif
1001 int num_ip4 = 0;
1002 int num_ip6 = 0;
1003
1004 memset(ip4ptrs, 0, sizeof(IPAddress*)*255);
1005 #if USE_IPV6
1006 memset(ip6ptrs, 0, sizeof(IPAddress*)*255);
1007 #endif
1008
1009 // for each unique address in list A - grab ptr
1010 for(t = 0; t < alen; t++) {
1011 if(aaddrs[t].IsIPv4()) {
1012 // check against IPv4 pruned list
1013 for(c = 0; c <= num_ip4; c++) {
1014 if(ip4ptrs[c] && aaddrs[t] == *(ip4ptrs[c]) ) break; // duplicate.
1015 }
1016 if(c > num_ip4) {
1017 ip4ptrs[num_ip4] = &aaddrs[t];
1018 num_ip4++;
1019 }
1020 }
1021 #if USE_IPV6
1022 else if(aaddrs[t].IsIPv6()) {
1023 debugs(14,8, HERE << "A[" << t << "]=IPv6 " << aaddrs[t]);
1024 // check against IPv6 pruned list
1025 for(c = 0; c <= num_ip6; c++) {
1026 if(ip6ptrs[c] && aaddrs[t] == *ip6ptrs[c]) break; // duplicate.
1027 }
1028 if(c > num_ip6) {
1029 ip6ptrs[num_ip6] = &aaddrs[t];
1030 num_ip6++;
1031 }
1032 }
1033 #endif
1034 }
1035
1036 // for each unique address in list B - grab ptr
1037 for(t = 0; t < blen; t++) {
1038 if(baddrs[t].IsIPv4()) {
1039 // check against IPv4 pruned list
1040 for(c = 0; c <= num_ip4; c++) {
1041 if(ip4ptrs[c] && baddrs[t] == *ip4ptrs[c]) break; // duplicate.
1042 }
1043 if(c > num_ip4) {
1044 ip4ptrs[num_ip4] = &baddrs[t];
1045 num_ip4++;
1046 }
1047 }
1048 #if USE_IPV6
1049 else if(baddrs[t].IsIPv6()) {
1050 // check against IPv6 pruned list
1051 for(c = 0; c <= num_ip6; c++) {
1052 if(ip6ptrs[c] && baddrs[t] == *ip6ptrs[c]) break; // duplicate.
1053 }
1054 if(c > num_ip6) {
1055 ip6ptrs[num_ip6] = &baddrs[t];
1056 num_ip6++;
1057 }
1058 }
1059 #endif
1060 }
1061
1062 fc = num_ip6 + num_ip4;
1063
1064 assert(fc > 0);
1065
1066 debugs(14, 5, "ipcacheMergeIPLists: Merge " << alen << "+" << blen << " into " << fc << " unique IPs.");
1067
1068 // copy the old IPs into the new list buffer.
1069 (*out) = (IPAddress*)xcalloc(fc, sizeof(IPAddress));
1070 outlen=0;
1071
1072 assert(out != NULL);
1073
1074 #if USE_IPV6
1075 /* IPv6 are preferred (tried first) over IPv4 */
1076
1077 for(int l = 0; outlen < num_ip6; l++, outlen++) {
1078 (*out)[outlen] = *ip6ptrs[l];
1079 debugs(14, 5, "ipcacheMergeIPLists: #" << outlen << " " << (*out)[outlen] );
1080 }
1081 #endif /* USE_IPV6 */
1082
1083 for(int l = 0; outlen < num_ip4; l++, outlen++) {
1084 (*out)[outlen] = *ip4ptrs[l];
1085 debugs(14, 5, "ipcacheMergeIPLists: #" << outlen << " " << (*out)[outlen] );
1086 }
1087
1088 assert(outlen == fc); // otherwise something broke badly!
1089 }
1090 #endif /* DNS_CNAME */
1091
1092 /// \ingroup IPCacheInternal
1093 /// Callback.
1094 static void
1095 ipcacheHandleCnameRecurse(const ipcache_addrs *addrs, void *cbdata)
1096 {
1097 #if DNS_CNAME
1098 ipcache_entry *i = NULL;
1099 char *pname = NULL;
1100 IPAddress *tmpbuf = NULL;
1101 int fc = 0;
1102 int ttl = 0;
1103 generic_cbdata* gcb = (generic_cbdata*)cbdata;
1104 // count of addrs at parent and child (REQ as .count is a char type!)
1105 int ccount = 0, pcount = 0;
1106
1107 debugs(14, 5, "ipcacheHandleCnameRecurse: Handling basic A/AAAA response.");
1108
1109 /* IFF no CNAME recursion being processed. do nothing. */
1110 if(cbdata == NULL)
1111 return;
1112
1113 gcb->unwrap(&i);
1114 assert(i != NULL);
1115
1116 // make sure we are actualy waiting for a CNAME callback to be run.
1117 assert(i->cname_wait > 0);
1118 // count this event. its being handled.
1119 i->cname_wait--;
1120
1121 pname = (char*)i->hash.key;
1122 assert(pname != NULL);
1123
1124 debugs(14, 5, "ipcacheHandleCnameRecurse: Handling CNAME recursion. CBDATA('" << gcb->data << "')='" << pname << "' -> " << std::hex << i);
1125
1126 if(i == NULL) {
1127 return; // Parent has expired. Don't merge, just leave for future Ref:
1128 }
1129
1130 /* IFF addrs is NULL (Usually an Error or Timeout occured on lookup.) */
1131 /* Ignore it and HOPE that we got some Additional records to use. */
1132 if(addrs == NULL)
1133 return;
1134
1135 ccount = (0+ addrs->count);
1136 pcount = (0+ i->addrs.count);
1137 ttl = i->expires;
1138
1139 /* IFF no CNAME results. do none of the processing BUT finish anyway. */
1140 if(addrs) {
1141
1142 debugs(14, 5, "ipcacheHandleCnameRecurse: Merge IP Lists for " << pname << " (" << pcount << "+" << ccount << ")");
1143
1144 /* add new IP records to entry */
1145 tmpbuf = i->addrs.in_addrs;
1146 i->addrs.in_addrs = NULL;
1147 ipcacheMergeIPLists(tmpbuf, pcount, addrs->in_addrs, ccount, &(i->addrs.in_addrs), fc);
1148 debugs(14,8, HERE << "in=" << tmpbuf << ", out=" << i->addrs.in_addrs );
1149 assert( (pcount>0 ? tmpbuf!=NULL : tmpbuf==NULL) );
1150 safe_free(tmpbuf);
1151
1152 if( pcount > 0) {
1153 /* IFF the parent initial lookup was given Additional records with A */
1154 // clear the 'bad IP mask'
1155 safe_free(i->addrs.bad_mask);
1156 }
1157 // create a new bad IP mask to fit the new size needed.
1158 if(fc > 0) {
1159 i->addrs.bad_mask = (unsigned char*)xcalloc(fc, sizeof(unsigned char));
1160 memset(i->addrs.bad_mask, 0, sizeof(unsigned char)*fc);
1161 }
1162
1163 if (fc < 256)
1164 i->addrs.count = (unsigned char) fc;
1165 else
1166 i->addrs.count = 255;
1167
1168 if (ttl == 0 || ttl > Config.positiveDnsTtl)
1169 ttl = Config.positiveDnsTtl;
1170
1171 if (ttl < Config.negativeDnsTtl)
1172 ttl = Config.negativeDnsTtl;
1173
1174 i->expires = squid_curtime + ttl;
1175
1176 i->flags.negcached = 0;
1177
1178 i->addrs.cur = 0;
1179
1180 i->addrs.badcount = 0;
1181 }
1182
1183 if(fc == 0) {
1184 i->error_message = xstrdup("No DNS Records");
1185 }
1186
1187 /* finish the lookup we were doing on parent when we got side-tracked for CNAME loop */
1188 if(i->cname_wait == 0) {
1189 ipcacheAddEntry(i);
1190 ipcacheCallback(i);
1191 }
1192 // else still more CNAME to be found.
1193 #endif /* DNS_CNAME */
1194 }
1195
1196 /// \ingroup IPCacheAPI
1197 void
1198 ipcacheInvalidate(const char *name)
1199 {
1200 ipcache_entry *i;
1201
1202 if ((i = ipcache_get(name)) == NULL)
1203 return;
1204
1205 i->expires = squid_curtime;
1206
1207 /*
1208 * NOTE, don't call ipcacheRelease here because we might be here due
1209 * to a thread started from a callback.
1210 */
1211 }
1212
1213 /// \ingroup IPCacheAPI
1214 void
1215 ipcacheInvalidateNegative(const char *name)
1216 {
1217 ipcache_entry *i;
1218
1219 if ((i = ipcache_get(name)) == NULL)
1220 return;
1221
1222 if (i->flags.negcached)
1223 i->expires = squid_curtime;
1224
1225 /*
1226 * NOTE, don't call ipcacheRelease here because we might be here due
1227 * to a thread started from a callback.
1228 */
1229 }
1230
1231 /// \ingroup IPCacheAPI
1232 ipcache_addrs *
1233 ipcacheCheckNumeric(const char *name)
1234 {
1235
1236 IPAddress ip;
1237 /* check if it's already a IP address in text form. */
1238
1239 /* it may be IPv6-wrapped */
1240 if(name[0] == '[') {
1241 char *tmp = xstrdup(&name[1]);
1242 tmp[strlen(tmp)-1] = '\0';
1243 if (!(ip = tmp)) {
1244 delete tmp;
1245 return NULL;
1246 }
1247 delete tmp;
1248 }
1249 else if (!(ip = name))
1250 return NULL;
1251
1252 debugs(14, 4, "ipcacheCheckNumeric: HIT_BYPASS for '" << name << "' == " << ip );
1253
1254 static_addrs.count = 1;
1255
1256 static_addrs.cur = 0;
1257
1258 static_addrs.in_addrs[0] = ip;
1259
1260 static_addrs.bad_mask[0] = FALSE;
1261
1262 static_addrs.badcount = 0;
1263
1264 return &static_addrs;
1265 }
1266
1267 /// \ingroup IPCacheInternal
1268 static void
1269 ipcacheLockEntry(ipcache_entry * i)
1270 {
1271 if (i->locks++ == 0) {
1272 dlinkDelete(&i->lru, &lru_list);
1273 dlinkAdd(i, &i->lru, &lru_list);
1274 }
1275 }
1276
1277 /// \ingroup IPCacheInternal
1278 static void
1279 ipcacheUnlockEntry(ipcache_entry * i)
1280 {
1281 if(i->locks < 1) {
1282 debugs(14, 1, "WARNING: ipcacheEntry unlocked with no lock! locks=" << i->locks);
1283 return;
1284 }
1285
1286 i->locks--;
1287
1288 if (ipcacheExpiredEntry(i))
1289 ipcacheRelease(i);
1290 }
1291
1292 /// \ingroup IPCacheAPI
1293 void
1294 ipcacheCycleAddr(const char *name, ipcache_addrs * ia)
1295 {
1296 ipcache_entry *i;
1297 unsigned char k;
1298 assert(name || ia);
1299
1300 if (NULL == ia) {
1301 if ((i = ipcache_get(name)) == NULL)
1302 return;
1303
1304 if (i->flags.negcached)
1305 return;
1306
1307 ia = &i->addrs;
1308 }
1309
1310 for (k = 0; k < ia->count; k++) {
1311 if (++ia->cur == ia->count)
1312 ia->cur = 0;
1313
1314 if (!ia->bad_mask[ia->cur])
1315 break;
1316 }
1317
1318 if (k == ia->count) {
1319 /* All bad, reset to All good */
1320 debugs(14, 3, "ipcacheCycleAddr: Changing ALL " << name << " addrs from BAD to OK");
1321
1322 for (k = 0; k < ia->count; k++)
1323 ia->bad_mask[k] = 0;
1324
1325 ia->badcount = 0;
1326
1327 ia->cur = 0;
1328 }
1329
1330 debugs(14, 3, "ipcacheCycleAddr: " << name << " now at " << ia->in_addrs[ia->cur] << " (" << ia->cur << " of " << ia->count << ")");
1331 }
1332
1333 /**
1334 \ingroup IPCacheAPI
1335 *
1336 \param name domain name to have an IP marked bad
1337 \param addr specific addres to be marked bad
1338 */
1339 void
1340 ipcacheMarkBadAddr(const char *name, IPAddress &addr)
1341 {
1342 ipcache_entry *i;
1343 ipcache_addrs *ia;
1344 int k;
1345
1346 /** Does nothing if the domain name does not exist. */
1347 if ((i = ipcache_get(name)) == NULL)
1348 return;
1349
1350 ia = &i->addrs;
1351
1352 for (k = 0; k < (int) ia->count; k++)
1353 {
1354 if (addr == ia->in_addrs[k] )
1355 break;
1356 }
1357
1358 /** Does nothing if the IP does not exist for the doamin. */
1359 if (k == (int) ia->count)
1360 return;
1361
1362 /** Marks the given address as BAD */
1363 if (!ia->bad_mask[k])
1364 {
1365 ia->bad_mask[k] = TRUE;
1366 ia->badcount++;
1367 i->expires = XMIN(squid_curtime + XMAX((time_t)60, Config.negativeDnsTtl), i->expires);
1368 debugs(14, 2, "ipcacheMarkBadAddr: " << name << " " << addr );
1369 }
1370
1371 /** then calls ipcacheCycleAddr() to advance the current pointer to the next OK address. */
1372 ipcacheCycleAddr(name, ia);
1373 }
1374
1375 /// \ingroup IPCacheAPI
1376 void
1377 ipcacheMarkGoodAddr(const char *name, IPAddress &addr)
1378 {
1379 ipcache_entry *i;
1380 ipcache_addrs *ia;
1381 int k;
1382
1383 if ((i = ipcache_get(name)) == NULL)
1384 return;
1385
1386 ia = &i->addrs;
1387
1388 for (k = 0; k < (int) ia->count; k++)
1389 {
1390 if (addr == ia->in_addrs[k])
1391 break;
1392 }
1393
1394 if (k == (int) ia->count) /* not found */
1395 return;
1396
1397 if (!ia->bad_mask[k]) /* already OK */
1398 return;
1399
1400 ia->bad_mask[k] = FALSE;
1401
1402 ia->badcount--;
1403
1404 debugs(14, 2, "ipcacheMarkGoodAddr: " << name << " " << addr );
1405 }
1406
1407 /// \ingroup IPCacheInternal
1408 static void
1409 ipcacheFreeEntry(void *data)
1410 {
1411 ipcache_entry *i = (ipcache_entry *)data;
1412 safe_free(i->addrs.in_addrs);
1413 safe_free(i->addrs.bad_mask);
1414 safe_free(i->hash.key);
1415 safe_free(i->error_message);
1416 memFree(i, MEM_IPCACHE_ENTRY);
1417 }
1418
1419 /// \ingroup IPCacheAPI
1420 void
1421 ipcacheFreeMemory(void)
1422 {
1423 hashFreeItems(ip_table, ipcacheFreeEntry);
1424 hashFreeMemory(ip_table);
1425 ip_table = NULL;
1426 }
1427
1428 /**
1429 \ingroup IPCacheAPI
1430 *
1431 * Recalculate IP cache size upon reconfigure.
1432 * Is called to clear the IPCache's data structures,
1433 * cancel all pending requests.
1434 */
1435 void
1436 ipcache_restart(void)
1437 {
1438 ipcache_high = (long) (((float) Config.ipcache.size *
1439 (float) Config.ipcache.high) / (float) 100);
1440 ipcache_low = (long) (((float) Config.ipcache.size *
1441 (float) Config.ipcache.low) / (float) 100);
1442 purge_entries_fromhosts();
1443 }
1444
1445 /**
1446 \ingroup IPCacheAPI
1447 *
1448 * Adds a "static" entry from /etc/hosts
1449 *
1450 \param name Hostname to be linked with IP
1451 \param ipaddr IP Address to be cached.
1452 *
1453 \retval 0 Success.
1454 \retval 1 IP address is invalid or other error.
1455 */
1456 int
1457 ipcacheAddEntryFromHosts(const char *name, const char *ipaddr)
1458 {
1459 ipcache_entry *i;
1460
1461 IPAddress ip;
1462
1463 if (!(ip = ipaddr)) {
1464 #if USE_IPV6
1465 if (strchr(ipaddr, ':') && strspn(ipaddr, "0123456789abcdefABCDEF:") == strlen(ipaddr)) {
1466 debugs(14, 3, "ipcacheAddEntryFromHosts: Skipping IPv6 address '" << ipaddr << "'");
1467 } else {
1468 debugs(14, 1, "ipcacheAddEntryFromHosts: Bad IP address '" << ipaddr << "'");
1469 }
1470 #else
1471 debugs(14, 1, "ipcacheAddEntryFromHosts: Bad IP address '" << ipaddr << "'");
1472 #endif
1473
1474 return 1;
1475 }
1476
1477 if ((i = ipcache_get(name))) {
1478 if (1 == i->flags.fromhosts) {
1479 ipcacheUnlockEntry(i);
1480 } else if (i->locks > 0) {
1481 debugs(14, 1, "ipcacheAddEntryFromHosts: can't add static entry for locked name '" << name << "'");
1482 return 1;
1483 } else {
1484 ipcacheRelease(i);
1485 }
1486 }
1487
1488 i = ipcacheCreateEntry(name);
1489 i->addrs.count = 1;
1490 i->addrs.cur = 0;
1491 i->addrs.badcount = 0;
1492
1493 i->addrs.in_addrs = (IPAddress *)xcalloc(1, sizeof(IPAddress));
1494 i->addrs.bad_mask = (unsigned char *)xcalloc(1, sizeof(unsigned char));
1495 i->addrs.in_addrs[0] = ip;
1496 i->addrs.bad_mask[0] = FALSE;
1497 i->flags.fromhosts = 1;
1498 ipcacheAddEntry(i);
1499 ipcacheLockEntry(i);
1500 return 0;
1501 }
1502
1503 #ifdef SQUID_SNMP
1504 /**
1505 \ingroup IPCacheAPI
1506 *
1507 * The function to return the ip cache statistics to via SNMP
1508 */
1509 variable_list *
1510 snmp_netIpFn(variable_list * Var, snint * ErrP)
1511 {
1512 variable_list *Answer = NULL;
1513 debugs(49, 5, "snmp_netIpFn: Processing request:");
1514 snmpDebugOid(5, Var->name, Var->name_length);
1515 *ErrP = SNMP_ERR_NOERROR;
1516
1517 switch (Var->name[LEN_SQ_NET + 1]) {
1518
1519 case IP_ENT:
1520 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1521 memInUse(MEM_IPCACHE_ENTRY),
1522 SMI_GAUGE32);
1523 break;
1524
1525 case IP_REQ:
1526 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1527 IpcacheStats.requests,
1528 SMI_COUNTER32);
1529 break;
1530
1531 case IP_HITS:
1532 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1533 IpcacheStats.hits,
1534 SMI_COUNTER32);
1535 break;
1536
1537 case IP_PENDHIT:
1538 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1539 0,
1540 SMI_GAUGE32);
1541 break;
1542
1543 case IP_NEGHIT:
1544 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1545 IpcacheStats.negative_hits,
1546 SMI_COUNTER32);
1547 break;
1548
1549 case IP_MISS:
1550 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1551 IpcacheStats.misses,
1552 SMI_COUNTER32);
1553 break;
1554
1555 case IP_GHBN:
1556 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1557 0, /* deprecated */
1558 SMI_COUNTER32);
1559 break;
1560
1561 case IP_LOC:
1562 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1563 0, /* deprecated */
1564 SMI_COUNTER32);
1565 break;
1566
1567 default:
1568 *ErrP = SNMP_ERR_NOSUCHNAME;
1569 snmp_var_free(Answer);
1570 return (NULL);
1571 }
1572
1573 return Answer;
1574 }
1575
1576 #endif /*SQUID_SNMP */