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