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