4 * DEBUG: section 35 FQDN Cache
5 * AUTHOR: Harvest Derived
7 * SQUID Web Proxy Cache http://www.squid-cache.org/
8 * ----------------------------------------------------------
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.
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.
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.
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.
38 #include "CacheManager.h"
39 #include "SquidTime.h"
44 \defgroup FQDNCacheAPI FQDN Cache API
46 \section Introduction Introduction
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).
55 \todo FQDN Cache should have its own API *.h file.
59 \defgroup FQDNCacheInternal FQDN Cache Internals
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.
72 * fqdncacheCallback() is called regularly to walk down the pending
73 * list and call handlers.
76 * LRU clean-up is performed through fqdncache_purgelru() according
77 * to the fqdncache_high threshold.
80 /// \ingroup FQDNCacheInternal
81 #define FQDN_LOW_WATER 90
83 /// \ingroup FQDNCacheInternal
84 #define FQDN_HIGH_WATER 95
86 /// \ingroup FQDNCacheAPI
87 typedef struct _fqdncache_entry fqdncache_entry
;
91 * The data structure used for storing name-address mappings
92 * is a small hashtable (static hash_table *fqdn_table),
93 * where structures of type fqdncache_entry whose most
94 * interesting members are:
96 struct _fqdncache_entry
{
97 hash_link hash
; /* must be first */
100 unsigned char name_count
;
101 char *names
[FQDN_MAX_NAMES
+ 1];
106 struct timeval request_time
;
108 unsigned short locks
;
111 unsigned int negcached
:1;
112 unsigned int fromhosts
:1;
116 /// \ingroup FQDNCacheInternal
117 static struct _fqdn_cache_stats
{
125 /// \ingroup FQDNCacheInternal
126 static dlink_list lru_list
;
129 static HLPCB fqdncacheHandleReply
;
130 static int fqdncacheParse(fqdncache_entry
*, const char *buf
);
132 static IDNSCB fqdncacheHandleReply
;
133 static int fqdncacheParse(fqdncache_entry
*, rfc1035_rr
*, int, const char *error_message
);
135 static void fqdncacheRelease(fqdncache_entry
*);
136 static fqdncache_entry
*fqdncacheCreateEntry(const char *name
);
137 static void fqdncacheCallback(fqdncache_entry
*);
138 static fqdncache_entry
*fqdncache_get(const char *);
139 static FQDNH dummy_handler
;
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
);
146 /// \ingroup FQDNCacheInternal
147 static hash_table
*fqdn_table
= NULL
;
149 /// \ingroup FQDNCacheInternal
150 static long fqdncache_low
= 180;
152 /// \ingroup FQDNCacheInternal
153 static long fqdncache_high
= 200;
156 \ingroup FQDNCacheInternal
157 * Removes the given fqdncache entry
160 fqdncacheRelease(fqdncache_entry
* f
)
163 hash_remove_link(fqdn_table
, (hash_link
*) f
);
165 for (k
= 0; k
< (int) f
->name_count
; k
++)
166 safe_free(f
->names
[k
]);
168 debugs(35, 5, "fqdncacheRelease: Released FQDN record for '" << hashKeyStr(&f
->hash
) << "'.");
170 dlinkDelete(&f
->lru
, &lru_list
);
172 safe_free(f
->hash
.key
);
174 safe_free(f
->error_message
);
176 memFree(f
, MEM_FQDNCACHE_ENTRY
);
180 \ingroup FQDNCacheInternal
181 \param name FQDN hash string.
182 \retval Match for given name
184 static fqdncache_entry
*
185 fqdncache_get(const char *name
)
188 static fqdncache_entry
*f
;
192 if ((e
= (hash_link
*)hash_lookup(fqdn_table
, name
)) != NULL
)
193 f
= (fqdncache_entry
*) e
;
199 /// \ingroup FQDNCacheInternal
201 fqdncacheExpiredEntry(const fqdncache_entry
* f
)
203 /* all static entries are locked, so this takes care of them too */
208 if (f
->expires
> squid_curtime
)
214 /// \ingroup FQDNCacheAPI
216 fqdncache_purgelru(void *notused
)
219 dlink_node
*prev
= NULL
;
222 eventAdd("fqdncache_purgelru", fqdncache_purgelru
, NULL
, 10.0, 1);
224 for (m
= lru_list
.tail
; m
; m
= prev
) {
225 if (memInUse(MEM_FQDNCACHE_ENTRY
) < fqdncache_low
)
230 f
= (fqdncache_entry
*)m
->data
;
240 debugs(35, 9, "fqdncache_purgelru: removed " << removed
<< " entries");
243 /// \ingroup FQDNCacheAPI
245 purge_entries_fromhosts(void)
247 dlink_node
*m
= lru_list
.head
;
248 fqdncache_entry
*i
= NULL
;
252 if (i
!= NULL
) { /* need to delay deletion */
253 fqdncacheRelease(i
); /* we just override locks */
257 t
= (fqdncache_entry
*)m
->data
;
259 if (t
->flags
.fromhosts
)
270 \ingroup FQDNCacheInternal
272 * Create blank fqdncache_entry
274 static fqdncache_entry
*
275 fqdncacheCreateEntry(const char *name
)
277 static fqdncache_entry
*f
;
278 f
= (fqdncache_entry
*)memAllocate(MEM_FQDNCACHE_ENTRY
);
279 f
->hash
.key
= xstrdup(name
);
280 f
->expires
= squid_curtime
+ Config
.negativeDnsTtl
;
284 /// \ingroup FQDNCacheInternal
286 fqdncacheAddEntry(fqdncache_entry
* f
)
288 hash_link
*e
= (hash_link
*)hash_lookup(fqdn_table
, f
->hash
.key
);
291 /* avoid colission */
292 fqdncache_entry
*q
= (fqdncache_entry
*) e
;
296 hash_join(fqdn_table
, &f
->hash
);
297 dlinkAdd(f
, &f
->lru
, &lru_list
);
298 f
->lastref
= squid_curtime
;
302 \ingroup FQDNCacheInternal
304 * Walks down the pending list, calling handlers
307 fqdncacheCallback(fqdncache_entry
* f
)
311 f
->lastref
= squid_curtime
;
316 fqdncacheLockEntry(f
);
318 callback
= f
->handler
;
322 if (cbdataReferenceValidDone(f
->handlerData
, &cbdata
)) {
323 dns_error_message
= f
->error_message
;
324 callback(f
->name_count
? f
->names
[0] : NULL
, cbdata
);
327 fqdncacheUnlockEntry(f
);
330 /// \ingroup FQDNCacheInternal
333 fqdncacheParse(fqdncache_entry
*f
, const char *inbuf
)
335 LOCAL_ARRAY(char, buf
, DNS_INBUF_SZ
);
338 const char *name
= (const char *)f
->hash
.key
;
339 f
->expires
= squid_curtime
+ Config
.negativeDnsTtl
;
340 f
->flags
.negcached
= 1;
343 debugs(35, 1, "fqdncacheParse: Got <NULL> reply in response to '" << name
<< "'");
344 f
->error_message
= xstrdup("Internal Error");
348 xstrncpy(buf
, inbuf
, DNS_INBUF_SZ
);
349 debugs(35, 5, "fqdncacheParse: parsing: {" << buf
<< "}");
350 token
= strtok(buf
, w_space
);
353 debugs(35, 1, "fqdncacheParse: Got <NULL>, expecting '$name' in response to '" << name
<< "'");
354 f
->error_message
= xstrdup("Internal Error");
358 if (0 == strcmp(token
, "$fail")) {
359 token
= strtok(NULL
, "\n");
360 assert(NULL
!= token
);
361 f
->error_message
= xstrdup(token
);
365 if (0 != strcmp(token
, "$name")) {
366 debugs(35, 1, "fqdncacheParse: Got '" << inbuf
<< "', expecting '$name' in response to '" << name
<< "'");
367 f
->error_message
= xstrdup("Internal Error");
371 token
= strtok(NULL
, w_space
);
374 debugs(35, 1, "fqdncacheParse: Got '" << inbuf
<< "', expecting TTL in response to '" << name
<< "'");
375 f
->error_message
= xstrdup("Internal Error");
381 token
= strtok(NULL
, w_space
);
384 debugs(35, 1, "fqdncacheParse: Got '" << inbuf
<< "', expecting hostname in response to '" << name
<< "'");
385 f
->error_message
= xstrdup("Internal Error");
389 f
->names
[0] = xstrdup(token
);
392 if (ttl
== 0 || ttl
> Config
.positiveDnsTtl
)
393 ttl
= Config
.positiveDnsTtl
;
395 if (ttl
< Config
.negativeDnsTtl
)
396 ttl
= Config
.negativeDnsTtl
;
398 f
->expires
= squid_curtime
+ ttl
;
400 f
->flags
.negcached
= 0;
402 return f
->name_count
;
407 fqdncacheParse(fqdncache_entry
*f
, rfc1035_rr
* answers
, int nr
, const char *error_message
)
411 const char *name
= (const char *)f
->hash
.key
;
412 f
->expires
= squid_curtime
+ Config
.negativeDnsTtl
;
413 f
->flags
.negcached
= 1;
416 debugs(35, 3, "fqdncacheParse: Lookup of '" << name
<< "' failed (" << error_message
<< ")");
417 f
->error_message
= xstrdup(error_message
);
422 debugs(35, 3, "fqdncacheParse: No DNS records for '" << name
<< "'");
423 f
->error_message
= xstrdup("No DNS records");
427 debugs(35, 3, "fqdncacheParse: " << nr
<< " answers for '" << name
<< "'");
430 for (k
= 0; k
< nr
; k
++) {
431 if (answers
[k
]._class
!= RFC1035_CLASS_IN
)
434 if (answers
[k
].type
== RFC1035_TYPE_PTR
) {
435 if (!answers
[k
].rdata
[0]) {
436 debugs(35, 2, "fqdncacheParse: blank PTR record for '" << name
<< "'");
440 if (strchr(answers
[k
].rdata
, ' ')) {
441 debugs(35, 2, "fqdncacheParse: invalid PTR record '" << answers
[k
].rdata
<< "' for '" << name
<< "'");
445 f
->names
[f
->name_count
++] = xstrdup(answers
[k
].rdata
);
446 } else if (answers
[k
].type
!= RFC1035_TYPE_CNAME
)
449 if (ttl
== 0 || (int) answers
[k
].ttl
< ttl
)
450 ttl
= answers
[k
].ttl
;
452 if (f
->name_count
>= FQDN_MAX_NAMES
)
456 if (f
->name_count
== 0) {
457 debugs(35, 1, "fqdncacheParse: No PTR record for '" << name
<< "'");
461 if (ttl
> Config
.positiveDnsTtl
)
462 ttl
= Config
.positiveDnsTtl
;
464 if (ttl
< Config
.negativeDnsTtl
)
465 ttl
= Config
.negativeDnsTtl
;
467 f
->expires
= squid_curtime
+ ttl
;
469 f
->flags
.negcached
= 0;
471 return f
->name_count
;
478 \ingroup FQDNCacheAPI
480 * Callback for handling DNS results.
484 fqdncacheHandleReply(void *data
, char *reply
)
486 fqdncacheHandleReply(void *data
, rfc1035_rr
* answers
, int na
, const char *error_message
)
491 static_cast<generic_cbdata
*>(data
)->unwrap(&f
);
492 n
= ++FqdncacheStats
.replies
;
493 statHistCount(&statCounter
.dns
.svc_time
,
494 tvSubMsec(f
->request_time
, current_time
));
497 fqdncacheParse(f
, reply
);
500 fqdncacheParse(f
, answers
, na
, error_message
);
503 fqdncacheAddEntry(f
);
505 fqdncacheCallback(f
);
509 \ingroup FQDNCacheAPI
511 \param addr IP address of domain to resolve.
512 \param handler A pointer to the function to be called when
513 * the reply from the FQDN cache
514 * (or the DNS if the FQDN cache misses)
515 \param handlerData Information that is passed to the handler
516 * and does not affect the FQDN cache.
519 fqdncache_nbgethostbyaddr(IpAddress
&addr
, FQDNH
* handler
, void *handlerData
)
521 fqdncache_entry
*f
= NULL
;
522 char name
[MAX_IPSTRLEN
];
525 addr
.NtoA(name
,MAX_IPSTRLEN
);
526 debugs(35, 4, "fqdncache_nbgethostbyaddr: Name '" << name
<< "'.");
527 FqdncacheStats
.requests
++;
529 if (name
[0] == '\0') {
530 debugs(35, 4, "fqdncache_nbgethostbyaddr: Invalid name!");
531 dns_error_message
= "Invalid hostname";
532 handler(NULL
, handlerData
);
536 f
= fqdncache_get(name
);
541 } else if (fqdncacheExpiredEntry(f
)) {
542 /* hit, but expired -- bummer */
547 debugs(35, 4, "fqdncache_nbgethostbyaddr: HIT for '" << name
<< "'");
549 if (f
->flags
.negcached
)
550 FqdncacheStats
.negative_hits
++;
552 FqdncacheStats
.hits
++;
554 f
->handler
= handler
;
556 f
->handlerData
= cbdataReference(handlerData
);
558 fqdncacheCallback(f
);
563 debugs(35, 5, "fqdncache_nbgethostbyaddr: MISS for '" << name
<< "'");
564 FqdncacheStats
.misses
++;
565 f
= fqdncacheCreateEntry(name
);
566 f
->handler
= handler
;
567 f
->handlerData
= cbdataReference(handlerData
);
568 f
->request_time
= current_time
;
569 c
= new generic_cbdata(f
);
572 dnsSubmit(hashKeyStr(&f
->hash
), fqdncacheHandleReply
, c
);
574 idnsPTRLookup(addr
, fqdncacheHandleReply
, c
);
578 /// \ingroup FQDNCacheInternal
580 fqdncacheRegisterWithCacheManager(void)
582 CacheManager::GetInstance()->
583 registerAction("fqdncache", "FQDN Cache Stats and Contents",
589 \ingroup FQDNCacheAPI
591 * Initialize the fqdncache.
592 * Called after IP cache initialization.
599 fqdncacheRegisterWithCacheManager();
604 debugs(35, 3, "Initializing FQDN Cache...");
606 memset(&FqdncacheStats
, '\0', sizeof(FqdncacheStats
));
608 memset(&lru_list
, '\0', sizeof(lru_list
));
610 fqdncache_high
= (long) (((float) Config
.fqdncache
.size
*
611 (float) FQDN_HIGH_WATER
) / (float) 100);
613 fqdncache_low
= (long) (((float) Config
.fqdncache
.size
*
614 (float) FQDN_LOW_WATER
) / (float) 100);
616 n
= hashPrime(fqdncache_high
/ 4);
618 fqdn_table
= hash_create((HASHCMP
*) strcmp
, n
, hash4
);
620 memDataInit(MEM_FQDNCACHE_ENTRY
, "fqdncache_entry",
621 sizeof(fqdncache_entry
), 0);
625 \ingroup FQDNCacheAPI
627 * Is different in that it only checks if an entry exists in
628 * it's data-structures and does not by default contact the
629 * DNS, unless this is requested, by setting the flags
630 * to FQDN_LOOKUP_IF_MISS.
632 \param addr address of the FQDN being resolved
633 \param flags values are NULL or FQDN_LOOKUP_IF_MISS. default is NULL.
637 fqdncache_gethostbyaddr(IpAddress
&addr
, int flags
)
639 char name
[MAX_IPSTRLEN
];
640 fqdncache_entry
*f
= NULL
;
642 assert(!addr
.IsAnyAddr() && !addr
.IsNoAddr());
643 addr
.NtoA(name
,MAX_IPSTRLEN
);
644 FqdncacheStats
.requests
++;
645 f
= fqdncache_get(name
);
649 } else if (fqdncacheExpiredEntry(f
)) {
652 } else if (f
->flags
.negcached
) {
653 FqdncacheStats
.negative_hits
++;
654 dns_error_message
= f
->error_message
;
657 FqdncacheStats
.hits
++;
658 f
->lastref
= squid_curtime
;
659 dns_error_message
= f
->error_message
;
663 dns_error_message
= NULL
;
665 FqdncacheStats
.misses
++;
667 if (flags
& FQDN_LOOKUP_IF_MISS
) {
668 fqdncache_nbgethostbyaddr(addr
, dummy_handler
, NULL
);
676 \ingroup FQDNCacheInternal
678 * Process objects list
681 fqdnStats(StoreEntry
* sentry
)
683 fqdncache_entry
*f
= NULL
;
687 if (fqdn_table
== NULL
)
690 storeAppendPrintf(sentry
, "FQDN Cache Statistics:\n");
692 storeAppendPrintf(sentry
, "FQDNcache Entries: %d\n",
693 memInUse(MEM_FQDNCACHE_ENTRY
));
695 storeAppendPrintf(sentry
, "FQDNcache Requests: %d\n",
696 FqdncacheStats
.requests
);
698 storeAppendPrintf(sentry
, "FQDNcache Hits: %d\n",
699 FqdncacheStats
.hits
);
701 storeAppendPrintf(sentry
, "FQDNcache Negative Hits: %d\n",
702 FqdncacheStats
.negative_hits
);
704 storeAppendPrintf(sentry
, "FQDNcache Misses: %d\n",
705 FqdncacheStats
.misses
);
707 storeAppendPrintf(sentry
, "FQDN Cache Contents:\n\n");
709 storeAppendPrintf(sentry
, "%-45.45s %3s %3s %3s %s\n",
710 "Address", "Flg", "TTL", "Cnt", "Hostnames");
712 hash_first(fqdn_table
);
714 while ((f
= (fqdncache_entry
*) hash_next(fqdn_table
))) {
715 ttl
= (f
->flags
.fromhosts
? -1 : (f
->expires
- squid_curtime
));
716 storeAppendPrintf(sentry
, "%-45.45s %c%c %3.3d % 3d",
717 hashKeyStr(&f
->hash
),
718 f
->flags
.negcached
? 'N' : ' ',
719 f
->flags
.fromhosts
? 'H' : ' ',
721 (int) f
->name_count
);
723 for (k
= 0; k
< (int) f
->name_count
; k
++)
724 storeAppendPrintf(sentry
, " %s", f
->names
[k
]);
726 storeAppendPrintf(sentry
, "\n");
730 /// \ingroup FQDNCacheInternal
732 dummy_handler(const char *bufnotused
, void *datanotused
)
737 /// \ingroup FQDNCacheAPI
739 fqdnFromAddr(IpAddress
&addr
)
742 static char buf
[MAX_IPSTRLEN
];
744 if (Config
.onoff
.log_fqdn
&& (n
= fqdncache_gethostbyaddr(addr
, 0)))
747 /// \todo Perhapse this should use toHostname() instead of straight NtoA.
748 /// that would wrap the IPv6 properly when raw.
749 addr
.NtoA(buf
, MAX_IPSTRLEN
);
754 /// \ingroup FQDNCacheInternal
756 fqdncacheLockEntry(fqdncache_entry
* f
)
758 if (f
->locks
++ == 0) {
759 dlinkDelete(&f
->lru
, &lru_list
);
760 dlinkAdd(f
, &f
->lru
, &lru_list
);
764 /// \ingroup FQDNCacheInternal
766 fqdncacheUnlockEntry(fqdncache_entry
* f
)
768 assert(f
->locks
> 0);
771 if (fqdncacheExpiredEntry(f
))
775 /// \ingroup FQDNCacheInternal
777 fqdncacheFreeEntry(void *data
)
779 fqdncache_entry
*f
= (fqdncache_entry
*)data
;
782 for (k
= 0; k
< (int) f
->name_count
; k
++)
783 safe_free(f
->names
[k
]);
785 safe_free(f
->hash
.key
);
787 safe_free(f
->error_message
);
789 memFree(f
, MEM_FQDNCACHE_ENTRY
);
792 /// \ingroup FQDNCacheAPI
794 fqdncacheFreeMemory(void)
796 hashFreeItems(fqdn_table
, fqdncacheFreeEntry
);
797 hashFreeMemory(fqdn_table
);
802 \ingroup FQDNCacheAPI
804 * Recalculate FQDN cache size upon reconfigure.
805 * Is called to clear the FQDN cache's data structures,
806 * cancel all pending requests.
809 fqdncache_restart(void)
811 fqdncache_high
= (long) (((float) Config
.fqdncache
.size
*
812 (float) FQDN_HIGH_WATER
) / (float) 100);
813 fqdncache_low
= (long) (((float) Config
.fqdncache
.size
*
814 (float) FQDN_LOW_WATER
) / (float) 100);
815 purge_entries_fromhosts();
819 \ingroup FQDNCacheAPI
821 * Adds a "static" entry from /etc/hosts.
823 * The worldist is to be managed by the caller,
824 * including pointed-to strings
826 \param addr FQDN name to be added.
830 fqdncacheAddEntryFromHosts(char *addr
, wordlist
* hostnames
)
832 fqdncache_entry
*fce
;
835 if ((fce
= fqdncache_get(addr
))) {
836 if (1 == fce
->flags
.fromhosts
) {
837 fqdncacheUnlockEntry(fce
);
838 } else if (fce
->locks
> 0) {
839 debugs(35, 1, "fqdncacheAddEntryFromHosts: can't add static entry for locked address '" << addr
<< "'");
842 fqdncacheRelease(fce
);
846 fce
= fqdncacheCreateEntry(addr
);
849 fce
->names
[j
] = xstrdup(hostnames
->key
);
851 hostnames
= hostnames
->next
;
853 if (j
>= FQDN_MAX_NAMES
)
858 fce
->names
[j
] = NULL
; /* it's safe */
859 fce
->flags
.fromhosts
= 1;
860 fqdncacheAddEntry(fce
);
861 fqdncacheLockEntry(fce
);
867 * \ingroup FQDNCacheAPI
868 * The function to return the FQDN statistics via SNMP
871 snmp_netFqdnFn(variable_list
* Var
, snint
* ErrP
)
873 variable_list
*Answer
= NULL
;
874 debugs(49, 5, "snmp_netFqdnFn: Processing request:");
875 snmpDebugOid(5, Var
->name
, Var
->name_length
);
876 *ErrP
= SNMP_ERR_NOERROR
;
878 switch (Var
->name
[LEN_SQ_NET
+ 1]) {
881 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
882 memInUse(MEM_FQDNCACHE_ENTRY
),
887 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
888 FqdncacheStats
.requests
,
893 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
899 /* this is now worthless */
900 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
906 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
907 FqdncacheStats
.negative_hits
,
912 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
913 FqdncacheStats
.misses
,
918 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
924 *ErrP
= SNMP_ERR_NOSUCHNAME
;
931 #endif /*SQUID_SNMP */
933 /// XXX: a hack to work around the missing DNS error info
934 // see http://www.squid-cache.org/bugs/show_bug.cgi?id=2459
936 dns_error_message_safe()
938 if (dns_error_message
)
939 return dns_error_message
;
940 debugs(35,DBG_IMPORTANT
, "Internal error: lost DNS error info");
941 return "lost DNS error";