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