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