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