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