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