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