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