]> git.ipfire.org Git - thirdparty/squid.git/blame - src/fqdncache.cc
Updates for running on squid-cache.org
[thirdparty/squid.git] / src / fqdncache.cc
CommitLineData
f88bb09c 1/*
63be0a78 2 * $Id: fqdncache.cc,v 1.178 2008/02/26 21:49:34 amosjeffries Exp $
f88bb09c 3 *
7cf620a9 4 * DEBUG: section 35 FQDN Cache
f88bb09c 5 * AUTHOR: Harvest Derived
6 *
2b6662ba 7 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 8 * ----------------------------------------------------------
f88bb09c 9 *
2b6662ba 10 * Squid is the result of efforts by numerous individuals from
11 * the Internet community; see the CONTRIBUTORS file for full
12 * details. Many organizations have provided support for Squid's
13 * development; see the SPONSORS file for full details. Squid is
14 * Copyrighted (C) 2001 by the Regents of the University of
15 * California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other
17 * sources; see the CREDITS file for full details.
f88bb09c 18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
cbdec147 31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 32 *
f88bb09c 33 */
34
35#include "squid.h"
aa839030 36#include "cbdata.h"
a553a5a3 37#include "event.h"
62ee09ca 38#include "CacheManager.h"
985c86bc 39#include "SquidTime.h"
e6ccf245 40#include "Store.h"
d295d770 41#include "wordlist.h"
f88bb09c 42
63be0a78 43/**
44 \defgroup FQDNCacheAPI FQDN Cache API
45 \ingroup Components
46 \section Introduction Introduction
47 \par
48 * The FQDN cache is a built-in component of squid providing
49 * Hostname to IP-Number translation functionality and managing
50 * the involved data-structures. Efficiency concerns require
51 * mechanisms that allow non-blocking access to these mappings.
52 * The FQDN cache usually doesn't block on a request except for
53 * special cases where this is desired (see below).
54 *
55 \todo FQDN Cache should have its own API *.h file.
56 */
57
58/**
59 \defgroup FQDNCacheInternal FQDN Cache Internals
60 \ingroup FQDNCacheAPI
61 \par
62 * Internally, the execution flow is as follows:
63 * On a miss, fqdncache_nbgethostbyaddr() checks whether a request
64 * for this name is already pending, and if positive, it creates a
65 * new entry using fqdncacheAddEntry(). Then it calls
66 * fqdncacheAddPending() to add a request to the queue together
67 * with data and handler. Else, ifqdncache_dnsDispatch() is called
68 * to directly create a DNS query or to fqdncacheEnqueue() if all
69 * no DNS port is free.
70 *
71 \par
72 * fqdncacheCallback() is called regularly to walk down the pending
73 * list and call handlers.
74 *
75 \par
76 * LRU clean-up is performed through fqdncache_purgelru() according
77 * to the fqdncache_high threshold.
78 */
79
80/// \ingroup FQDNCacheInternal
f88bb09c 81#define FQDN_LOW_WATER 90
63be0a78 82
83/// \ingroup FQDNCacheInternal
f88bb09c 84#define FQDN_HIGH_WATER 95
f88bb09c 85
63be0a78 86/// \ingroup FQDNCacheAPI
add5d21f 87typedef struct _fqdncache_entry fqdncache_entry;
88
63be0a78 89/**
90 \ingroup FQDNCacheAPI
91 * The data structure used for storing name-address mappings
92 * is a small hashtable (static hash_table *fqdn_table),
93 * where structures of type fqdncache_entry whose most
94 * interesting members are:
95 */
62e76326 96struct _fqdncache_entry
97{
186477c1 98 hash_link hash; /* must be first */
add5d21f 99 time_t lastref;
100 time_t expires;
101 unsigned char name_count;
102 char *names[FQDN_MAX_NAMES + 1];
103 FQDNH *handler;
104 void *handlerData;
105 char *error_message;
62e76326 106
add5d21f 107 struct timeval request_time;
108 dlink_node lru;
ac06f720 109 unsigned short locks;
62e76326 110
111 struct
112 {
3d0ac046
HN
113 unsigned int negcached:1;
114 unsigned int fromhosts:1;
2fadd50d 115 } flags;
add5d21f 116};
117
63be0a78 118/// \ingroup FQDNCacheInternal
119static struct _fqdn_cache_stats
62e76326 120{
f88bb09c 121 int requests;
122 int replies;
123 int hits;
124 int misses;
f88bb09c 125 int negative_hits;
2fadd50d 126} FqdncacheStats;
f88bb09c 127
63be0a78 128/// \ingroup FQDNCacheInternal
4bc76d59 129static dlink_list lru_list;
130
59f34d62 131#if USE_DNSSERVERS
74addf6c 132static HLPCB fqdncacheHandleReply;
7ba8d0b8 133static int fqdncacheParse(fqdncache_entry *, const char *buf);
59f34d62 134#else
135static IDNSCB fqdncacheHandleReply;
7ba8d0b8 136static int fqdncacheParse(fqdncache_entry *, rfc1035_rr *, int, const char *error_message);
59f34d62 137#endif
add5d21f 138static void fqdncacheRelease(fqdncache_entry *);
139static fqdncache_entry *fqdncacheCreateEntry(const char *name);
140static void fqdncacheCallback(fqdncache_entry *);
f5b8bbc4 141static fqdncache_entry *fqdncache_get(const char *);
348b2031 142static FQDNH dummy_handler;
f5b8bbc4 143static int fqdncacheExpiredEntry(const fqdncache_entry *);
f5b8bbc4 144static void fqdncacheLockEntry(fqdncache_entry * f);
145static void fqdncacheUnlockEntry(fqdncache_entry * f);
ec878047 146static FREE fqdncacheFreeEntry;
add5d21f 147static void fqdncacheAddEntry(fqdncache_entry * f);
f88bb09c 148
63be0a78 149/// \ingroup FQDNCacheInternal
365e5b34 150static hash_table *fqdn_table = NULL;
f88bb09c 151
63be0a78 152/// \ingroup FQDNCacheInternal
24382924 153static long fqdncache_low = 180;
63be0a78 154
155/// \ingroup FQDNCacheInternal
24382924 156static long fqdncache_high = 200;
f88bb09c 157
63be0a78 158/**
159 \ingroup FQDNCacheInternal
160 * Removes the given fqdncache entry
161 */
b8d8561b 162static void
add5d21f 163fqdncacheRelease(fqdncache_entry * f)
f88bb09c 164{
f88bb09c 165 int k;
3fda0827 166 hash_remove_link(fqdn_table, (hash_link *) f);
62e76326 167
add5d21f 168 for (k = 0; k < (int) f->name_count; k++)
62e76326 169 safe_free(f->names[k]);
170
bf8fe701 171 debugs(35, 5, "fqdncacheRelease: Released FQDN record for '" << hashKeyStr(&f->hash) << "'.");
62e76326 172
4bc76d59 173 dlinkDelete(&f->lru, &lru_list);
62e76326 174
186477c1 175 safe_free(f->hash.key);
62e76326 176
429fdbec 177 safe_free(f->error_message);
62e76326 178
db1cd23c 179 memFree(f, MEM_FQDNCACHE_ENTRY);
f88bb09c 180}
181
63be0a78 182/**
183 \ingroup FQDNCacheInternal
184 \param name FQDN hash string.
185 \retval Match for given name
186 */
b8d8561b 187static fqdncache_entry *
0ee4272b 188fqdncache_get(const char *name)
f88bb09c 189{
190 hash_link *e;
191 static fqdncache_entry *f;
f88bb09c 192 f = NULL;
62e76326 193
f88bb09c 194 if (fqdn_table) {
62e76326 195 if ((e = (hash_link *)hash_lookup(fqdn_table, name)) != NULL)
196 f = (fqdncache_entry *) e;
f88bb09c 197 }
62e76326 198
f88bb09c 199 return f;
200}
201
63be0a78 202/// \ingroup FQDNCacheInternal
b8d8561b 203static int
fe4e214f 204fqdncacheExpiredEntry(const fqdncache_entry * f)
f88bb09c 205{
0e70aa1e 206 /* all static entries are locked, so this takes care of them too */
62e76326 207
429fdbec 208 if (f->locks != 0)
62e76326 209 return 0;
210
e84703ad 211 if (f->expires > squid_curtime)
62e76326 212 return 0;
213
f88bb09c 214 return 1;
215}
216
63be0a78 217/// \ingroup FQDNCacheAPI
59c4d35b 218void
219fqdncache_purgelru(void *notused)
f88bb09c 220{
4bc76d59 221 dlink_node *m;
222 dlink_node *prev = NULL;
223 fqdncache_entry *f;
f88bb09c 224 int removed = 0;
52040193 225 eventAdd("fqdncache_purgelru", fqdncache_purgelru, NULL, 10.0, 1);
62e76326 226
4bc76d59 227 for (m = lru_list.tail; m; m = prev) {
62e76326 228 if (memInUse(MEM_FQDNCACHE_ENTRY) < fqdncache_low)
229 break;
230
231 prev = m->prev;
232
233 f = (fqdncache_entry *)m->data;
234
235 if (f->locks != 0)
236 continue;
237
238 fqdncacheRelease(f);
239
240 removed++;
f88bb09c 241 }
62e76326 242
bf8fe701 243 debugs(35, 9, "fqdncache_purgelru: removed " << removed << " entries");
f88bb09c 244}
245
63be0a78 246/// \ingroup FQDNCacheAPI
0e70aa1e 247static void
248purge_entries_fromhosts(void)
249{
250 dlink_node *m = lru_list.head;
251 fqdncache_entry *i = NULL;
252 fqdncache_entry *t;
62e76326 253
0e70aa1e 254 while (m) {
62e76326 255 if (i != NULL) { /* need to delay deletion */
256 fqdncacheRelease(i); /* we just override locks */
257 i = NULL;
258 }
259
260 t = (fqdncache_entry *)m->data;
261
262 if (t->flags.fromhosts)
263 i = t;
264
265 m = m->next;
0e70aa1e 266 }
62e76326 267
0e70aa1e 268 if (i != NULL)
62e76326 269 fqdncacheRelease(i);
0e70aa1e 270}
271
63be0a78 272/**
273 \ingroup FQDNCacheInternal
274 *
275 * Create blank fqdncache_entry
276 */
b8d8561b 277static fqdncache_entry *
add5d21f 278fqdncacheCreateEntry(const char *name)
f88bb09c 279{
4bc76d59 280 static fqdncache_entry *f;
e6ccf245 281 f = (fqdncache_entry *)memAllocate(MEM_FQDNCACHE_ENTRY);
186477c1 282 f->hash.key = xstrdup(name);
4bc76d59 283 f->expires = squid_curtime + Config.negativeDnsTtl;
4bc76d59 284 return f;
f88bb09c 285}
286
63be0a78 287/// \ingroup FQDNCacheInternal
b8d8561b 288static void
add5d21f 289fqdncacheAddEntry(fqdncache_entry * f)
f88bb09c 290{
e6ccf245 291 hash_link *e = (hash_link *)hash_lookup(fqdn_table, f->hash.key);
62e76326 292
add5d21f 293 if (NULL != e) {
62e76326 294 /* avoid colission */
295 fqdncache_entry *q = (fqdncache_entry *) e;
296 fqdncacheRelease(q);
429fdbec 297 }
62e76326 298
186477c1 299 hash_join(fqdn_table, &f->hash);
add5d21f 300 dlinkAdd(f, &f->lru, &lru_list);
429fdbec 301 f->lastref = squid_curtime;
f88bb09c 302}
303
63be0a78 304/**
305 \ingroup FQDNCacheInternal
306 *
307 * Walks down the pending list, calling handlers
308 */
b8d8561b 309static void
add5d21f 310fqdncacheCallback(fqdncache_entry * f)
f88bb09c 311{
fa80a8ef 312 FQDNH *callback;
313 void *cbdata;
f88bb09c 314 f->lastref = squid_curtime;
62e76326 315
fa80a8ef 316 if (!f->handler)
62e76326 317 return;
318
5a2aa048 319 fqdncacheLockEntry(f);
62e76326 320
fa80a8ef 321 callback = f->handler;
62e76326 322
add5d21f 323 f->handler = NULL;
62e76326 324
fa80a8ef 325 if (cbdataReferenceValidDone(f->handlerData, &cbdata)) {
62e76326 326 dns_error_message = f->error_message;
d6d3dbd4 327 callback(f->name_count ? f->names[0] : NULL, cbdata);
f88bb09c 328 }
62e76326 329
429fdbec 330 fqdncacheUnlockEntry(f);
f88bb09c 331}
332
63be0a78 333/// \ingroup FQDNCacheInternal
b18baa48 334#if USE_DNSSERVERS
7ba8d0b8 335static int
336fqdncacheParse(fqdncache_entry *f, const char *inbuf)
f88bb09c 337{
bd34f258 338 LOCAL_ARRAY(char, buf, DNS_INBUF_SZ);
e84703ad 339 char *token;
bd34f258 340 int ttl;
7ba8d0b8 341 const char *name = (const char *)f->hash.key;
342 f->expires = squid_curtime + Config.negativeDnsTtl;
343 f->flags.negcached = 1;
62e76326 344
c68e9c6b 345 if (inbuf == NULL) {
bf8fe701 346 debugs(35, 1, "fqdncacheParse: Got <NULL> reply in response to '" << name << "'");
7ba8d0b8 347 f->error_message = xstrdup("Internal Error");
348 return -1;
c68e9c6b 349 }
62e76326 350
c579f632 351 xstrncpy(buf, inbuf, DNS_INBUF_SZ);
bf8fe701 352 debugs(35, 5, "fqdncacheParse: parsing: {" << buf << "}");
bd34f258 353 token = strtok(buf, w_space);
62e76326 354
bd34f258 355 if (NULL == token) {
bf8fe701 356 debugs(35, 1, "fqdncacheParse: Got <NULL>, expecting '$name' in response to '" << name << "'");
7ba8d0b8 357 f->error_message = xstrdup("Internal Error");
358 return -1;
bd34f258 359 }
62e76326 360
bd34f258 361 if (0 == strcmp(token, "$fail")) {
62e76326 362 token = strtok(NULL, "\n");
363 assert(NULL != token);
7ba8d0b8 364 f->error_message = xstrdup(token);
365 return 0;
bd34f258 366 }
62e76326 367
bd34f258 368 if (0 != strcmp(token, "$name")) {
bf8fe701 369 debugs(35, 1, "fqdncacheParse: Got '" << inbuf << "', expecting '$name' in response to '" << name << "'");
7ba8d0b8 370 f->error_message = xstrdup("Internal Error");
371 return -1;
bd34f258 372 }
62e76326 373
bd34f258 374 token = strtok(NULL, w_space);
62e76326 375
bd34f258 376 if (NULL == token) {
bf8fe701 377 debugs(35, 1, "fqdncacheParse: Got '" << inbuf << "', expecting TTL in response to '" << name << "'");
7ba8d0b8 378 f->error_message = xstrdup("Internal Error");
379 return -1;
bd34f258 380 }
62e76326 381
bd34f258 382 ttl = atoi(token);
62e76326 383
bd34f258 384 token = strtok(NULL, w_space);
62e76326 385
7ba8d0b8 386 if (NULL == token) {
bf8fe701 387 debugs(35, 1, "fqdncacheParse: Got '" << inbuf << "', expecting hostname in response to '" << name << "'");
7ba8d0b8 388 f->error_message = xstrdup("Internal Error");
389 return -1;
f88bb09c 390 }
62e76326 391
7ba8d0b8 392 f->names[0] = xstrdup(token);
393 f->name_count = 1;
394
395 if (ttl == 0 || ttl > Config.positiveDnsTtl)
396 ttl = Config.positiveDnsTtl;
397
398 if (ttl < Config.negativeDnsTtl)
399 ttl = Config.negativeDnsTtl;
400
401 f->expires = squid_curtime + ttl;
402
403 f->flags.negcached = 0;
404
405 return f->name_count;
f88bb09c 406}
62e76326 407
59f34d62 408#else
7ba8d0b8 409static int
410fqdncacheParse(fqdncache_entry *f, rfc1035_rr * answers, int nr, const char *error_message)
59f34d62 411{
59f34d62 412 int k;
7ba8d0b8 413 int ttl = 0;
414 const char *name = (const char *)f->hash.key;
415 f->expires = squid_curtime + Config.negativeDnsTtl;
416 f->flags.negcached = 1;
62e76326 417
59f34d62 418 if (nr < 0) {
bf8fe701 419 debugs(35, 3, "fqdncacheParse: Lookup of '" << name << "' failed (" << error_message << ")");
7ba8d0b8 420 f->error_message = xstrdup(error_message);
421 return -1;
59f34d62 422 }
62e76326 423
59f34d62 424 if (nr == 0) {
bf8fe701 425 debugs(35, 3, "fqdncacheParse: No DNS records for '" << name << "'");
7ba8d0b8 426 f->error_message = xstrdup("No DNS records");
427 return 0;
59f34d62 428 }
62e76326 429
bf8fe701 430 debugs(35, 3, "fqdncacheParse: " << nr << " answers for '" << name << "'");
59f34d62 431 assert(answers);
62e76326 432
1810dde6 433 for (k = 0; k < nr; k++) {
62e76326 434 if (answers[k]._class != RFC1035_CLASS_IN)
435 continue;
436
2d320a3a 437 if (answers[k].type == RFC1035_TYPE_PTR) {
438 if (!answers[k].rdata[0]) {
bf8fe701 439 debugs(35, 2, "fqdncacheParse: blank PTR record for '" << name << "'");
2d320a3a 440 continue;
441 }
470cd749 442
2d320a3a 443 if (strchr(answers[k].rdata, ' ')) {
bf8fe701 444 debugs(35, 2, "fqdncacheParse: invalid PTR record '" << answers[k].rdata << "' for '" << name << "'");
2d320a3a 445 continue;
446 }
447
448 f->names[f->name_count++] = xstrdup(answers[k].rdata);
449 } else if (answers[k].type != RFC1035_TYPE_CNAME)
450 continue;
62e76326 451
7ba8d0b8 452 if (ttl == 0 || (int) answers[k].ttl < ttl)
453 ttl = answers[k].ttl;
62e76326 454
7ba8d0b8 455 if (f->name_count >= FQDN_MAX_NAMES)
456 break;
457 }
62e76326 458
7ba8d0b8 459 if (f->name_count == 0) {
bf8fe701 460 debugs(35, 1, "fqdncacheParse: No PTR record for '" << name << "'");
7ba8d0b8 461 return 0;
462 }
62e76326 463
3e8c4107 464 if (ttl > Config.positiveDnsTtl)
7ba8d0b8 465 ttl = Config.positiveDnsTtl;
62e76326 466
7ba8d0b8 467 if (ttl < Config.negativeDnsTtl)
468 ttl = Config.negativeDnsTtl;
469
470 f->expires = squid_curtime + ttl;
62e76326 471
7ba8d0b8 472 f->flags.negcached = 0;
473
474 return f->name_count;
59f34d62 475}
62e76326 476
59f34d62 477#endif
f88bb09c 478
aa839030 479
63be0a78 480/**
481 \ingroup FQDNCacheAPI
482 *
483 * Callback for handling DNS results.
484 */
429fdbec 485static void
59f34d62 486#if USE_DNSSERVERS
74addf6c 487fqdncacheHandleReply(void *data, char *reply)
59f34d62 488#else
7ba8d0b8 489fqdncacheHandleReply(void *data, rfc1035_rr * answers, int na, const char *error_message)
59f34d62 490#endif
f88bb09c 491{
f88bb09c 492 int n;
aa839030 493 fqdncache_entry *f;
494 static_cast<generic_cbdata *>(data)->unwrap(&f);
c68e9c6b 495 n = ++FqdncacheStats.replies;
83704487 496 statHistCount(&statCounter.dns.svc_time,
62e76326 497 tvSubMsec(f->request_time, current_time));
59f34d62 498#if USE_DNSSERVERS
62e76326 499
7ba8d0b8 500 fqdncacheParse(f, reply);
59f34d62 501#else
62e76326 502
7ba8d0b8 503 fqdncacheParse(f, answers, na, error_message);
59f34d62 504#endif
62e76326 505
add5d21f 506 fqdncacheAddEntry(f);
62e76326 507
add5d21f 508 fqdncacheCallback(f);
f88bb09c 509}
510
63be0a78 511/**
512 \ingroup FQDNCacheAPI
513 *
514 \param addr IP address of domain to resolve.
515 \param handler A pointer to the function to be called when
516 * the reply from the FQDN cache
517 * (or the DNS if the FQDN cache misses)
518 \param handlerData Information that is passed to the handler
519 * and does not affect the FQDN cache.
520 */
429fdbec 521void
cc192b50 522fqdncache_nbgethostbyaddr(IPAddress &addr, FQDNH * handler, void *handlerData)
f88bb09c 523{
524 fqdncache_entry *f = NULL;
cc192b50 525 char name[MAX_IPSTRLEN];
74addf6c 526 generic_cbdata *c;
698e6f16 527 assert(handler);
cc192b50 528 addr.NtoA(name,MAX_IPSTRLEN);
bf8fe701 529 debugs(35, 4, "fqdncache_nbgethostbyaddr: Name '" << name << "'.");
f88bb09c 530 FqdncacheStats.requests++;
62e76326 531
cc192b50 532 if (name[0] == '\0')
62e76326 533 {
bf8fe701 534 debugs(35, 4, "fqdncache_nbgethostbyaddr: Invalid name!");
2ffff82e 535 dns_error_message = "Invalid hostname";
62e76326 536 handler(NULL, handlerData);
537 return;
f88bb09c 538 }
62e76326 539
add5d21f 540 f = fqdncache_get(name);
62e76326 541
542 if (NULL == f)
543 {
544 /* miss */
545 (void) 0;
546 } else if (fqdncacheExpiredEntry(f))
547 {
548 /* hit, but expired -- bummer */
549 fqdncacheRelease(f);
550 f = NULL;
551 } else
552 {
553 /* hit */
bf8fe701 554 debugs(35, 4, "fqdncache_nbgethostbyaddr: HIT for '" << name << "'");
62e76326 555
556 if (f->flags.negcached)
557 FqdncacheStats.negative_hits++;
558 else
559 FqdncacheStats.hits++;
560
561 f->handler = handler;
562
563 f->handlerData = cbdataReference(handlerData);
564
565 fqdncacheCallback(f);
566
567 return;
f88bb09c 568 }
add5d21f 569
bf8fe701 570 debugs(35, 5, "fqdncache_nbgethostbyaddr: MISS for '" << name << "'");
add5d21f 571 FqdncacheStats.misses++;
572 f = fqdncacheCreateEntry(name);
573 f->handler = handler;
fa80a8ef 574 f->handlerData = cbdataReference(handlerData);
add5d21f 575 f->request_time = current_time;
aa839030 576 c = new generic_cbdata(f);
59f34d62 577#if USE_DNSSERVERS
62e76326 578
186477c1 579 dnsSubmit(hashKeyStr(&f->hash), fqdncacheHandleReply, c);
59f34d62 580#else
581 idnsPTRLookup(addr, fqdncacheHandleReply, c);
582#endif
f88bb09c 583}
584
5f5e883f
FC
585/// \ingroup FQDNCacheInternal
586static void
587fqdncacheRegisterWithCacheManager(void)
588{
589 CacheManager::GetInstance()->
590 registerAction("fqdncache", "FQDN Cache Stats and Contents",
591 fqdnStats, 0, 1);
592
593}
594
63be0a78 595/**
596 \ingroup FQDNCacheAPI
597 *
598 * Initialize the fqdncache.
599 * Called after IP cache initialization.
600 */
b8d8561b 601void
0673c0ba 602fqdncache_init(void)
f88bb09c 603{
aa9e2cab 604 int n;
62e76326 605
ea391f18
FC
606 fqdncacheRegisterWithCacheManager();
607
19054954 608 if (fqdn_table)
62e76326 609 return;
610
bf8fe701 611 debugs(35, 3, "Initializing FQDN Cache...");
62e76326 612
f88bb09c 613 memset(&FqdncacheStats, '\0', sizeof(FqdncacheStats));
62e76326 614
3eb55834 615 memset(&lru_list, '\0', sizeof(lru_list));
62e76326 616
e55650e3 617 fqdncache_high = (long) (((float) Config.fqdncache.size *
62e76326 618 (float) FQDN_HIGH_WATER) / (float) 100);
619
e55650e3 620 fqdncache_low = (long) (((float) Config.fqdncache.size *
62e76326 621 (float) FQDN_LOW_WATER) / (float) 100);
622
aa9e2cab 623 n = hashPrime(fqdncache_high / 4);
62e76326 624
30abd221 625 fqdn_table = hash_create((HASHCMP *) strcmp, n, hash4);
62e76326 626
add5d21f 627 memDataInit(MEM_FQDNCACHE_ENTRY, "fqdncache_entry",
62e76326 628 sizeof(fqdncache_entry), 0);
f88bb09c 629}
630
63be0a78 631/**
632 \ingroup FQDNCacheAPI
633 *
634 * Is different in that it only checks if an entry exists in
635 * it's data-structures and does not by default contact the
636 * DNS, unless this is requested, by setting the flags
637 * to FQDN_LOOKUP_IF_MISS.
638 *
639 \param addr address of the FQDN being resolved
640 \param flags values are NULL or FQDN_LOOKUP_IF_MISS. default is NULL.
641 *
642 */
0ee4272b 643const char *
cc192b50 644fqdncache_gethostbyaddr(IPAddress &addr, int flags)
f88bb09c 645{
cc192b50 646 char name[MAX_IPSTRLEN];
f88bb09c 647 fqdncache_entry *f = NULL;
62e76326 648
cc192b50 649 assert(!addr.IsAnyAddr() && !addr.IsNoAddr());
650 addr.NtoA(name,MAX_IPSTRLEN);
f88bb09c 651 FqdncacheStats.requests++;
add5d21f 652 f = fqdncache_get(name);
62e76326 653
654 if (NULL == f)
655 {
656 (void) 0;
657 } else if (fqdncacheExpiredEntry(f))
658 {
659 fqdncacheRelease(f);
660 f = NULL;
661 } else if (f->flags.negcached)
662 {
663 FqdncacheStats.negative_hits++;
664 dns_error_message = f->error_message;
665 return NULL;
666 } else
667 {
668 FqdncacheStats.hits++;
669 f->lastref = squid_curtime;
2ffff82e 670 dns_error_message = f->error_message;
62e76326 671 return f->names[0];
f88bb09c 672 }
62e76326 673
2ffff82e 674 dns_error_message = NULL;
675
429fdbec 676 FqdncacheStats.misses++;
62e76326 677
f88bb09c 678 if (flags & FQDN_LOOKUP_IF_MISS)
cc192b50 679 {
62e76326 680 fqdncache_nbgethostbyaddr(addr, dummy_handler, NULL);
cc192b50 681 }
62e76326 682
f88bb09c 683 return NULL;
684}
685
686
63be0a78 687/**
688 \ingroup FQDNCacheInternal
689 *
690 * Process objects list
691 */
b8d8561b 692void
693fqdnStats(StoreEntry * sentry)
f88bb09c 694{
695 fqdncache_entry *f = NULL;
696 int k;
697 int ttl;
62e76326 698
4bc76d59 699 if (fqdn_table == NULL)
62e76326 700 return;
701
15576b6a 702 storeAppendPrintf(sentry, "FQDN Cache Statistics:\n");
62e76326 703
15576b6a 704 storeAppendPrintf(sentry, "FQDNcache Entries: %d\n",
62e76326 705 memInUse(MEM_FQDNCACHE_ENTRY));
706
15576b6a 707 storeAppendPrintf(sentry, "FQDNcache Requests: %d\n",
62e76326 708 FqdncacheStats.requests);
709
15576b6a 710 storeAppendPrintf(sentry, "FQDNcache Hits: %d\n",
62e76326 711 FqdncacheStats.hits);
712
15576b6a 713 storeAppendPrintf(sentry, "FQDNcache Negative Hits: %d\n",
62e76326 714 FqdncacheStats.negative_hits);
715
15576b6a 716 storeAppendPrintf(sentry, "FQDNcache Misses: %d\n",
62e76326 717 FqdncacheStats.misses);
718
15576b6a 719 storeAppendPrintf(sentry, "FQDN Cache Contents:\n\n");
62e76326 720
cc192b50 721 storeAppendPrintf(sentry, "%-45.45s %3s %3s %3s %s\n",
62e76326 722 "Address", "Flg", "TTL", "Cnt", "Hostnames");
723
0f6bebac 724 hash_first(fqdn_table);
62e76326 725
0f6bebac 726 while ((f = (fqdncache_entry *) hash_next(fqdn_table))) {
62e76326 727 ttl = (f->flags.fromhosts ? -1 : (f->expires - squid_curtime));
cc192b50 728 storeAppendPrintf(sentry, "%-45.45s %c%c %3.3d % 3d",
62e76326 729 hashKeyStr(&f->hash),
730 f->flags.negcached ? 'N' : ' ',
731 f->flags.fromhosts ? 'H' : ' ',
732 ttl,
733 (int) f->name_count);
734
735 for (k = 0; k < (int) f->name_count; k++)
736 storeAppendPrintf(sentry, " %s", f->names[k]);
737
738 storeAppendPrintf(sentry, "\n");
f88bb09c 739 }
f88bb09c 740}
741
63be0a78 742/// \ingroup FQDNCacheInternal
b8d8561b 743static void
79d39a72 744dummy_handler(const char *bufnotused, void *datanotused)
f88bb09c 745{
746 return;
747}
748
63be0a78 749/// \ingroup FQDNCacheAPI
0ee4272b 750const char *
cc192b50 751fqdnFromAddr(IPAddress &addr)
28ab0c0a 752{
0ee4272b 753 const char *n;
cc192b50 754 static char buf[MAX_IPSTRLEN];
62e76326 755
17a0a4ee 756 if (Config.onoff.log_fqdn && (n = fqdncache_gethostbyaddr(addr, 0)))
62e76326 757 return n;
758
63be0a78 759/// \todo Perhapse this should use toHostname() instead of straight NtoA.
5ad35449 760/// that would wrap the IPv6 properly when raw.
cc192b50 761 addr.NtoA(buf, MAX_IPSTRLEN);
62e76326 762
39de381a 763 return buf;
28ab0c0a 764}
f201f309 765
63be0a78 766/// \ingroup FQDNCacheInternal
429fdbec 767static void
768fqdncacheLockEntry(fqdncache_entry * f)
769{
4bc76d59 770 if (f->locks++ == 0) {
62e76326 771 dlinkDelete(&f->lru, &lru_list);
772 dlinkAdd(f, &f->lru, &lru_list);
4bc76d59 773 }
429fdbec 774}
775
63be0a78 776/// \ingroup FQDNCacheInternal
429fdbec 777static void
778fqdncacheUnlockEntry(fqdncache_entry * f)
779{
ac06f720 780 assert(f->locks > 0);
429fdbec 781 f->locks--;
62e76326 782
429fdbec 783 if (fqdncacheExpiredEntry(f))
62e76326 784 fqdncacheRelease(f);
429fdbec 785}
786
63be0a78 787/// \ingroup FQDNCacheInternal
ec878047 788static void
789fqdncacheFreeEntry(void *data)
790{
e6ccf245 791 fqdncache_entry *f = (fqdncache_entry *)data;
ec878047 792 int k;
62e76326 793
ec878047 794 for (k = 0; k < (int) f->name_count; k++)
62e76326 795 safe_free(f->names[k]);
796
186477c1 797 safe_free(f->hash.key);
62e76326 798
ec878047 799 safe_free(f->error_message);
62e76326 800
db1cd23c 801 memFree(f, MEM_FQDNCACHE_ENTRY);
ec878047 802}
803
63be0a78 804/// \ingroup FQDNCacheAPI
56e15c50 805void
806fqdncacheFreeMemory(void)
807{
ec878047 808 hashFreeItems(fqdn_table, fqdncacheFreeEntry);
56e15c50 809 hashFreeMemory(fqdn_table);
afe95a7e 810 fqdn_table = NULL;
56e15c50 811}
429fdbec 812
63be0a78 813/**
814 \ingroup FQDNCacheAPI
815 *
816 * Recalculate FQDN cache size upon reconfigure.
817 * Is called to clear the FQDN cache's data structures,
818 * cancel all pending requests.
819 */
429fdbec 820void
821fqdncache_restart(void)
822{
e55650e3 823 fqdncache_high = (long) (((float) Config.fqdncache.size *
62e76326 824 (float) FQDN_HIGH_WATER) / (float) 100);
e55650e3 825 fqdncache_low = (long) (((float) Config.fqdncache.size *
62e76326 826 (float) FQDN_LOW_WATER) / (float) 100);
0e70aa1e 827 purge_entries_fromhosts();
828}
829
63be0a78 830/**
831 \ingroup FQDNCacheAPI
832 *
833 * Adds a "static" entry from /etc/hosts.
834 \par
835 * The worldist is to be managed by the caller,
836 * including pointed-to strings
837 *
838 \param addr FQDN name to be added.
839 \param hostnames ??
0e70aa1e 840 */
841void
842fqdncacheAddEntryFromHosts(char *addr, wordlist * hostnames)
843{
844 fqdncache_entry *fce;
845 int j = 0;
62e76326 846
0e70aa1e 847 if ((fce = fqdncache_get(addr))) {
62e76326 848 if (1 == fce->flags.fromhosts) {
849 fqdncacheUnlockEntry(fce);
850 } else if (fce->locks > 0) {
bf8fe701 851 debugs(35, 1, "fqdncacheAddEntryFromHosts: can't add static entry for locked address '" << addr << "'");
62e76326 852 return;
853 } else {
854 fqdncacheRelease(fce);
855 }
0e70aa1e 856 }
62e76326 857
0e70aa1e 858 fce = fqdncacheCreateEntry(addr);
62e76326 859
0e70aa1e 860 while (hostnames) {
62e76326 861 fce->names[j] = xstrdup(hostnames->key);
862 j++;
863 hostnames = hostnames->next;
864
865 if (j >= FQDN_MAX_NAMES)
866 break;
0e70aa1e 867 }
62e76326 868
0e70aa1e 869 fce->name_count = j;
870 fce->names[j] = NULL; /* it's safe */
871 fce->flags.fromhosts = 1;
872 fqdncacheAddEntry(fce);
873 fqdncacheLockEntry(fce);
429fdbec 874}
ce75f381 875
0e70aa1e 876
ce75f381 877#ifdef SQUID_SNMP
63be0a78 878/**
879 * \ingroup FQDNCacheAPI
880 * The function to return the FQDN statistics via SNMP
135171fe 881 */
86115da5 882variable_list *
e7ef99a7 883snmp_netFqdnFn(variable_list * Var, snint * ErrP)
d60c11be 884{
736eb6ad 885 variable_list *Answer = NULL;
bf8fe701 886 debugs(49, 5, "snmp_netFqdnFn: Processing request:");
e7ef99a7 887 snmpDebugOid(5, Var->name, Var->name_length);
86115da5 888 *ErrP = SNMP_ERR_NOERROR;
62e76326 889
135171fe 890 switch (Var->name[LEN_SQ_NET + 1]) {
62e76326 891
e7ef99a7 892 case FQDN_ENT:
62e76326 893 Answer = snmp_var_new_integer(Var->name, Var->name_length,
894 memInUse(MEM_FQDNCACHE_ENTRY),
895 SMI_GAUGE32);
896 break;
897
e7ef99a7 898 case FQDN_REQ:
62e76326 899 Answer = snmp_var_new_integer(Var->name, Var->name_length,
900 FqdncacheStats.requests,
901 SMI_COUNTER32);
902 break;
903
e7ef99a7 904 case FQDN_HITS:
62e76326 905 Answer = snmp_var_new_integer(Var->name, Var->name_length,
906 FqdncacheStats.hits,
907 SMI_COUNTER32);
908 break;
909
e7ef99a7 910 case FQDN_PENDHIT:
62e76326 911 /* this is now worthless */
912 Answer = snmp_var_new_integer(Var->name, Var->name_length,
913 0,
914 SMI_GAUGE32);
915 break;
916
e7ef99a7 917 case FQDN_NEGHIT:
62e76326 918 Answer = snmp_var_new_integer(Var->name, Var->name_length,
919 FqdncacheStats.negative_hits,
920 SMI_COUNTER32);
921 break;
922
e7ef99a7 923 case FQDN_MISS:
62e76326 924 Answer = snmp_var_new_integer(Var->name, Var->name_length,
925 FqdncacheStats.misses,
926 SMI_COUNTER32);
927 break;
928
e7ef99a7 929 case FQDN_GHBN:
62e76326 930 Answer = snmp_var_new_integer(Var->name, Var->name_length,
931 0, /* deprecated */
932 SMI_COUNTER32);
933 break;
934
ce75f381 935 default:
62e76326 936 *ErrP = SNMP_ERR_NOSUCHNAME;
937 break;
86115da5 938 }
62e76326 939
86115da5 940 return Answer;
ce75f381 941}
e7ef99a7 942
135171fe 943#endif /*SQUID_SNMP */
de48c85e
AR
944
945/// XXX: a hack to work around the missing DNS error info
946// see http://www.squid-cache.org/bugs/show_bug.cgi?id=2459
947const char *
948dns_error_message_safe()
949{
950 if (dns_error_message)
951 return dns_error_message;
952 debugs(35,DBG_IMPORTANT, "Internal error: lost DNS error info");
953 return "lost DNS error";
954}