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