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