]> git.ipfire.org Git - thirdparty/squid.git/blob - src/fqdncache.cc
Upgrade comm layer Connection handling
[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 "Store.h"
42 #include "wordlist.h"
43
44 /**
45 \defgroup FQDNCacheAPI FQDN Cache API
46 \ingroup Components
47 \section Introduction Introduction
48 \par
49 * The FQDN cache is a built-in component of squid providing
50 * Hostname to IP-Number translation functionality and managing
51 * the involved data-structures. Efficiency concerns require
52 * mechanisms that allow non-blocking access to these mappings.
53 * The FQDN cache usually doesn't block on a request except for
54 * special cases where this is desired (see below).
55 *
56 \todo FQDN Cache should have its own API *.h file.
57 */
58
59 /**
60 \defgroup FQDNCacheInternal FQDN Cache Internals
61 \ingroup FQDNCacheAPI
62 \par
63 * Internally, the execution flow is as follows:
64 * On a miss, fqdncache_nbgethostbyaddr() checks whether a request
65 * for this name is already pending, and if positive, it creates a
66 * new entry using fqdncacheAddEntry(). Then it calls
67 * fqdncacheAddPending() to add a request to the queue together
68 * with data and handler. Else, ifqdncache_dnsDispatch() is called
69 * to directly create a DNS query or to fqdncacheEnqueue() if all
70 * no DNS port is free.
71 *
72 \par
73 * fqdncacheCallback() is called regularly to walk down the pending
74 * list and call handlers.
75 *
76 \par
77 * LRU clean-up is performed through fqdncache_purgelru() according
78 * to the fqdncache_high threshold.
79 */
80
81 /// \ingroup FQDNCacheInternal
82 #define FQDN_LOW_WATER 90
83
84 /// \ingroup FQDNCacheInternal
85 #define FQDN_HIGH_WATER 95
86
87 /**
88 \ingroup FQDNCacheAPI
89 * The data structure used for storing name-address mappings
90 * is a small hashtable (static hash_table *fqdn_table),
91 * where structures of type fqdncache_entry whose most
92 * interesting members are:
93 */
94 class fqdncache_entry
95 {
96 public:
97 hash_link hash; /* must be first */
98 time_t lastref;
99 time_t expires;
100 unsigned char name_count;
101 char *names[FQDN_MAX_NAMES + 1];
102 FQDNH *handler;
103 void *handlerData;
104 char *error_message;
105
106 struct timeval request_time;
107 dlink_node lru;
108 unsigned short locks;
109
110 struct {
111 unsigned int negcached:1;
112 unsigned int fromhosts:1;
113 } flags;
114
115 int age() const; ///< time passed since request_time or -1 if unknown
116 };
117
118 /// \ingroup FQDNCacheInternal
119 static struct _fqdn_cache_stats {
120 int requests;
121 int replies;
122 int hits;
123 int misses;
124 int negative_hits;
125 } FqdncacheStats;
126
127 /// \ingroup FQDNCacheInternal
128 static dlink_list lru_list;
129
130 #if USE_DNSSERVERS
131 static HLPCB fqdncacheHandleReply;
132 static int fqdncacheParse(fqdncache_entry *, const char *buf);
133 #else
134 static IDNSCB fqdncacheHandleReply;
135 static int fqdncacheParse(fqdncache_entry *, rfc1035_rr *, int, const char *error_message);
136 #endif
137 static void fqdncacheRelease(fqdncache_entry *);
138 static fqdncache_entry *fqdncacheCreateEntry(const char *name);
139 static void fqdncacheCallback(fqdncache_entry *, int wait);
140 static fqdncache_entry *fqdncache_get(const char *);
141 static int fqdncacheExpiredEntry(const fqdncache_entry *);
142 static void fqdncacheLockEntry(fqdncache_entry * f);
143 static void fqdncacheUnlockEntry(fqdncache_entry * f);
144 static FREE fqdncacheFreeEntry;
145 static void fqdncacheAddEntry(fqdncache_entry * f);
146
147 /// \ingroup FQDNCacheInternal
148 static hash_table *fqdn_table = NULL;
149
150 /// \ingroup FQDNCacheInternal
151 static long fqdncache_low = 180;
152
153 /// \ingroup FQDNCacheInternal
154 static long fqdncache_high = 200;
155
156 /// \ingroup FQDNCacheInternal
157 inline int fqdncacheCount() { return fqdn_table ? fqdn_table->count : 0; }
158
159 int
160 fqdncache_entry::age() const
161 {
162 return request_time.tv_sec ? tvSubMsec(request_time, current_time) : -1;
163 }
164
165
166 /**
167 \ingroup FQDNCacheInternal
168 * Removes the given fqdncache entry
169 */
170 static void
171 fqdncacheRelease(fqdncache_entry * f)
172 {
173 int k;
174 hash_remove_link(fqdn_table, (hash_link *) f);
175
176 for (k = 0; k < (int) f->name_count; k++)
177 safe_free(f->names[k]);
178
179 debugs(35, 5, "fqdncacheRelease: Released FQDN record for '" << hashKeyStr(&f->hash) << "'.");
180
181 dlinkDelete(&f->lru, &lru_list);
182
183 safe_free(f->hash.key);
184
185 safe_free(f->error_message);
186
187 memFree(f, MEM_FQDNCACHE_ENTRY);
188 }
189
190 /**
191 \ingroup FQDNCacheInternal
192 \param name FQDN hash string.
193 \retval Match for given name
194 */
195 static fqdncache_entry *
196 fqdncache_get(const char *name)
197 {
198 hash_link *e;
199 static fqdncache_entry *f;
200 f = NULL;
201
202 if (fqdn_table) {
203 if ((e = (hash_link *)hash_lookup(fqdn_table, name)) != NULL)
204 f = (fqdncache_entry *) e;
205 }
206
207 return f;
208 }
209
210 /// \ingroup FQDNCacheInternal
211 static int
212 fqdncacheExpiredEntry(const fqdncache_entry * f)
213 {
214 /* all static entries are locked, so this takes care of them too */
215
216 if (f->locks != 0)
217 return 0;
218
219 if (f->expires > squid_curtime)
220 return 0;
221
222 return 1;
223 }
224
225 /// \ingroup FQDNCacheAPI
226 void
227 fqdncache_purgelru(void *notused)
228 {
229 dlink_node *m;
230 dlink_node *prev = NULL;
231 fqdncache_entry *f;
232 int removed = 0;
233 eventAdd("fqdncache_purgelru", fqdncache_purgelru, NULL, 10.0, 1);
234
235 for (m = lru_list.tail; m; m = prev) {
236 if (fqdncacheCount() < fqdncache_low)
237 break;
238
239 prev = m->prev;
240
241 f = (fqdncache_entry *)m->data;
242
243 if (f->locks != 0)
244 continue;
245
246 fqdncacheRelease(f);
247
248 removed++;
249 }
250
251 debugs(35, 9, "fqdncache_purgelru: removed " << removed << " entries");
252 }
253
254 /// \ingroup FQDNCacheAPI
255 static void
256 purge_entries_fromhosts(void)
257 {
258 dlink_node *m = lru_list.head;
259 fqdncache_entry *i = NULL;
260 fqdncache_entry *t;
261
262 while (m) {
263 if (i != NULL) { /* need to delay deletion */
264 fqdncacheRelease(i); /* we just override locks */
265 i = NULL;
266 }
267
268 t = (fqdncache_entry *)m->data;
269
270 if (t->flags.fromhosts)
271 i = t;
272
273 m = m->next;
274 }
275
276 if (i != NULL)
277 fqdncacheRelease(i);
278 }
279
280 /**
281 \ingroup FQDNCacheInternal
282 *
283 * Create blank fqdncache_entry
284 */
285 static fqdncache_entry *
286 fqdncacheCreateEntry(const char *name)
287 {
288 static fqdncache_entry *f;
289 f = (fqdncache_entry *)memAllocate(MEM_FQDNCACHE_ENTRY);
290 f->hash.key = xstrdup(name);
291 f->expires = squid_curtime + Config.negativeDnsTtl;
292 return f;
293 }
294
295 /// \ingroup FQDNCacheInternal
296 static void
297 fqdncacheAddEntry(fqdncache_entry * f)
298 {
299 hash_link *e = (hash_link *)hash_lookup(fqdn_table, f->hash.key);
300
301 if (NULL != e) {
302 /* avoid colission */
303 fqdncache_entry *q = (fqdncache_entry *) e;
304 fqdncacheRelease(q);
305 }
306
307 hash_join(fqdn_table, &f->hash);
308 dlinkAdd(f, &f->lru, &lru_list);
309 f->lastref = squid_curtime;
310 }
311
312 /**
313 \ingroup FQDNCacheInternal
314 *
315 * Walks down the pending list, calling handlers
316 */
317 static void
318 fqdncacheCallback(fqdncache_entry * f, int wait)
319 {
320 FQDNH *callback;
321 void *cbdata;
322 f->lastref = squid_curtime;
323
324 if (!f->handler)
325 return;
326
327 fqdncacheLockEntry(f);
328
329 callback = f->handler;
330
331 f->handler = NULL;
332
333 if (cbdataReferenceValidDone(f->handlerData, &cbdata)) {
334 const DnsLookupDetails details(f->error_message, wait);
335 callback(f->name_count ? f->names[0] : NULL, details, cbdata);
336 }
337
338 fqdncacheUnlockEntry(f);
339 }
340
341 /// \ingroup FQDNCacheInternal
342 #if USE_DNSSERVERS
343 static int
344 fqdncacheParse(fqdncache_entry *f, const char *inbuf)
345 {
346 LOCAL_ARRAY(char, buf, DNS_INBUF_SZ);
347 char *token;
348 int ttl;
349 const char *name = (const char *)f->hash.key;
350 f->expires = squid_curtime + Config.negativeDnsTtl;
351 f->flags.negcached = 1;
352
353 if (inbuf == NULL) {
354 debugs(35, 1, "fqdncacheParse: Got <NULL> reply in response to '" << name << "'");
355 f->error_message = xstrdup("Internal Error");
356 return -1;
357 }
358
359 xstrncpy(buf, inbuf, DNS_INBUF_SZ);
360 debugs(35, 5, "fqdncacheParse: parsing: {" << buf << "}");
361 token = strtok(buf, w_space);
362
363 if (NULL == token) {
364 debugs(35, 1, "fqdncacheParse: Got <NULL>, expecting '$name' in response to '" << name << "'");
365 f->error_message = xstrdup("Internal Error");
366 return -1;
367 }
368
369 if (0 == strcmp(token, "$fail")) {
370 token = strtok(NULL, "\n");
371 assert(NULL != token);
372 f->error_message = xstrdup(token);
373 return 0;
374 }
375
376 if (0 != strcmp(token, "$name")) {
377 debugs(35, 1, "fqdncacheParse: Got '" << inbuf << "', expecting '$name' in response to '" << name << "'");
378 f->error_message = xstrdup("Internal Error");
379 return -1;
380 }
381
382 token = strtok(NULL, w_space);
383
384 if (NULL == token) {
385 debugs(35, 1, "fqdncacheParse: Got '" << inbuf << "', expecting TTL in response to '" << name << "'");
386 f->error_message = xstrdup("Internal Error");
387 return -1;
388 }
389
390 ttl = atoi(token);
391
392 token = strtok(NULL, w_space);
393
394 if (NULL == token) {
395 debugs(35, 1, "fqdncacheParse: Got '" << inbuf << "', expecting hostname in response to '" << name << "'");
396 f->error_message = xstrdup("Internal Error");
397 return -1;
398 }
399
400 f->names[0] = xstrdup(token);
401 f->name_count = 1;
402
403 if (ttl == 0 || ttl > Config.positiveDnsTtl)
404 ttl = Config.positiveDnsTtl;
405
406 if (ttl < Config.negativeDnsTtl)
407 ttl = Config.negativeDnsTtl;
408
409 f->expires = squid_curtime + ttl;
410
411 f->flags.negcached = 0;
412
413 return f->name_count;
414 }
415
416 #else
417 static int
418 fqdncacheParse(fqdncache_entry *f, rfc1035_rr * answers, int nr, const char *error_message)
419 {
420 int k;
421 int ttl = 0;
422 const char *name = (const char *)f->hash.key;
423 f->expires = squid_curtime + Config.negativeDnsTtl;
424 f->flags.negcached = 1;
425
426 if (nr < 0) {
427 debugs(35, 3, "fqdncacheParse: Lookup of '" << name << "' failed (" << error_message << ")");
428 f->error_message = xstrdup(error_message);
429 return -1;
430 }
431
432 if (nr == 0) {
433 debugs(35, 3, "fqdncacheParse: No DNS records for '" << name << "'");
434 f->error_message = xstrdup("No DNS records");
435 return 0;
436 }
437
438 debugs(35, 3, "fqdncacheParse: " << nr << " answers for '" << name << "'");
439 assert(answers);
440
441 for (k = 0; k < nr; k++) {
442 if (answers[k]._class != RFC1035_CLASS_IN)
443 continue;
444
445 if (answers[k].type == RFC1035_TYPE_PTR) {
446 if (!answers[k].rdata[0]) {
447 debugs(35, 2, "fqdncacheParse: blank PTR record for '" << name << "'");
448 continue;
449 }
450
451 if (strchr(answers[k].rdata, ' ')) {
452 debugs(35, 2, "fqdncacheParse: invalid PTR record '" << answers[k].rdata << "' for '" << name << "'");
453 continue;
454 }
455
456 f->names[f->name_count++] = xstrdup(answers[k].rdata);
457 } else if (answers[k].type != RFC1035_TYPE_CNAME)
458 continue;
459
460 if (ttl == 0 || (int) answers[k].ttl < ttl)
461 ttl = answers[k].ttl;
462
463 if (f->name_count >= FQDN_MAX_NAMES)
464 break;
465 }
466
467 if (f->name_count == 0) {
468 debugs(35, 1, "fqdncacheParse: No PTR record for '" << name << "'");
469 return 0;
470 }
471
472 if (ttl > Config.positiveDnsTtl)
473 ttl = Config.positiveDnsTtl;
474
475 if (ttl < Config.negativeDnsTtl)
476 ttl = Config.negativeDnsTtl;
477
478 f->expires = squid_curtime + ttl;
479
480 f->flags.negcached = 0;
481
482 return f->name_count;
483 }
484
485 #endif
486
487
488 /**
489 \ingroup FQDNCacheAPI
490 *
491 * Callback for handling DNS results.
492 */
493 static void
494 #if USE_DNSSERVERS
495 fqdncacheHandleReply(void *data, char *reply)
496 #else
497 fqdncacheHandleReply(void *data, rfc1035_rr * answers, int na, const char *error_message)
498 #endif
499 {
500 fqdncache_entry *f;
501 static_cast<generic_cbdata *>(data)->unwrap(&f);
502 ++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 */