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