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