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