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