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