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