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