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