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