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