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