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