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