1 /*
2 * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 /* DEBUG: section 35 FQDN Cache */
10
11 #include "squid.h"
12 #include "cbdata.h"
13 #include "dns/forward.h"
14 #include "dns/LookupDetails.h"
15 #include "dns/rfc1035.h"
16 #include "event.h"
17 #include "fqdncache.h"
18 #include "helper.h"
19 #include "mgr/Registration.h"
20 #include "SquidConfig.h"
21 #include "SquidTime.h"
22 #include "StatCounters.h"
23 #include "Store.h"
24 #include "util.h"
25
26 #if SQUID_SNMP
27 #include "snmp_core.h"
28 #endif
29
30 /**
31 \defgroup FQDNCacheAPI FQDN Cache API
32 \ingroup Components
33 \section Introduction Introduction
34 \par
35 * The FQDN cache is a built-in component of squid providing
36 * Hostname to IP-Number translation functionality and managing
37 * the involved data-structures. Efficiency concerns require
38 * mechanisms that allow non-blocking access to these mappings.
39 * The FQDN cache usually doesn't block on a request except for
40 * special cases where this is desired (see below).
41 *
42 \todo FQDN Cache should have its own API *.h file.
43 */
44
45 /**
46 \defgroup FQDNCacheInternal FQDN Cache Internals
47 \ingroup FQDNCacheAPI
48 \par
49 * Internally, the execution flow is as follows:
50 * On a miss, fqdncache_nbgethostbyaddr() checks whether a request
51 * for this name is already pending, and if positive, it creates a
52 * new entry using fqdncacheAddEntry(). Then it calls
53 * fqdncacheAddPending() to add a request to the queue together
54 * with data and handler. Else, ifqdncache_dnsDispatch() is called
55 * to directly create a DNS query or to fqdncacheEnqueue() if all
56 * no DNS port is free.
57 *
58 \par
59 * fqdncacheCallback() is called regularly to walk down the pending
60 * list and call handlers.
61 *
62 \par
63 * LRU clean-up is performed through fqdncache_purgelru() according
64 * to the fqdncache_high threshold.
65 */
66
67 /// \ingroup FQDNCacheInternal
68 #define FQDN_LOW_WATER 90
69
70 /// \ingroup FQDNCacheInternal
71 #define FQDN_HIGH_WATER 95
72
73 /**
74 * The data structure used for storing name-address mappings
75 * is a small hashtable (static hash_table *fqdn_table),
76 * where structures of type fqdncache_entry whose most
77 * interesting members are:
78 */
79 class fqdncache_entry
80 {
81 MEMPROXY_CLASS(fqdncache_entry);
82
83 public:
84 fqdncache_entry(const char *name);
85 ~fqdncache_entry();
86
87 hash_link hash; /* must be first */
88 time_t lastref;
89 time_t expires;
90 unsigned char name_count;
91 char *names[FQDN_MAX_NAMES + 1];
92 FQDNH *handler;
93 void *handlerData;
94 char *error_message;
95
96 struct timeval request_time;
97 dlink_node lru;
98 unsigned short locks;
99
100 struct Flags {
Flagsfqdncache_entry::Flags101 Flags() : negcached(false), fromhosts(false) {}
102
103 bool negcached;
104 bool fromhosts;
105 } flags;
106
107 int age() const; ///< time passed since request_time or -1 if unknown
108 };
109
110 /// \ingroup FQDNCacheInternal
111 static struct _fqdn_cache_stats {
112 int requests;
113 int replies;
114 int hits;
115 int misses;
116 int negative_hits;
117 } FqdncacheStats;
118
119 /// \ingroup FQDNCacheInternal
120 static dlink_list lru_list;
121
122 static IDNSCB fqdncacheHandleReply;
123 static int fqdncacheParse(fqdncache_entry *, const rfc1035_rr *, int, const char *error_message);
124 static void fqdncacheRelease(fqdncache_entry *);
125 static void fqdncacheCallback(fqdncache_entry *, int wait);
126 static fqdncache_entry *fqdncache_get(const char *);
127 static int fqdncacheExpiredEntry(const fqdncache_entry *);
128 static void fqdncacheLockEntry(fqdncache_entry * f);
129 static void fqdncacheUnlockEntry(fqdncache_entry * f);
130 static FREE fqdncacheFreeEntry;
131 static void fqdncacheAddEntry(fqdncache_entry * f);
132
133 /// \ingroup FQDNCacheInternal
134 static hash_table *fqdn_table = NULL;
135
136 /// \ingroup FQDNCacheInternal
137 static long fqdncache_low = 180;
138
139 /// \ingroup FQDNCacheInternal
140 static long fqdncache_high = 200;
141
142 /// \ingroup FQDNCacheInternal
fqdncacheCount()143 inline int fqdncacheCount() { return fqdn_table ? fqdn_table->count : 0; }
144
145 int
age() const146 fqdncache_entry::age() const
147 {
148 return request_time.tv_sec ? tvSubMsec(request_time, current_time) : -1;
149 }
150
151 /**
152 \ingroup FQDNCacheInternal
153 * Removes the given fqdncache entry
154 */
155 static void
fqdncacheRelease(fqdncache_entry * f)156 fqdncacheRelease(fqdncache_entry * f)
157 {
158 hash_remove_link(fqdn_table, (hash_link *) f);
159 debugs(35, 5, "fqdncacheRelease: Released FQDN record for '" << hashKeyStr(&f->hash) << "'.");
160 dlinkDelete(&f->lru, &lru_list);
161 delete f;
162 }
163
164 /**
165 \ingroup FQDNCacheInternal
166 \param name FQDN hash string.
167 \retval Match for given name
168 */
169 static fqdncache_entry *
fqdncache_get(const char * name)170 fqdncache_get(const char *name)
171 {
172 hash_link *e;
173 static fqdncache_entry *f;
174 f = NULL;
175
176 if (fqdn_table) {
177 if ((e = (hash_link *)hash_lookup(fqdn_table, name)) != NULL)
178 f = (fqdncache_entry *) e;
179 }
180
181 return f;
182 }
183
184 /// \ingroup FQDNCacheInternal
185 static int
fqdncacheExpiredEntry(const fqdncache_entry * f)186 fqdncacheExpiredEntry(const fqdncache_entry * f)
187 {
188 /* all static entries are locked, so this takes care of them too */
189
190 if (f->locks != 0)
191 return 0;
192
193 if (f->expires > squid_curtime)
194 return 0;
195
196 return 1;
197 }
198
199 /// \ingroup FQDNCacheAPI
200 void
fqdncache_purgelru(void *)201 fqdncache_purgelru(void *)
202 {
203 dlink_node *m;
204 dlink_node *prev = NULL;
205 fqdncache_entry *f;
206 int removed = 0;
207 eventAdd("fqdncache_purgelru", fqdncache_purgelru, NULL, 10.0, 1);
208
209 for (m = lru_list.tail; m; m = prev) {
210 if (fqdncacheCount() < fqdncache_low)
211 break;
212
213 prev = m->prev;
214
215 f = (fqdncache_entry *)m->data;
216
217 if (f->locks != 0)
218 continue;
219
220 fqdncacheRelease(f);
221
222 ++removed;
223 }
224
225 debugs(35, 9, "fqdncache_purgelru: removed " << removed << " entries");
226 }
227
228 /// \ingroup FQDNCacheAPI
229 static void
purge_entries_fromhosts(void)230 purge_entries_fromhosts(void)
231 {
232 dlink_node *m = lru_list.head;
233 fqdncache_entry *i = NULL;
234 fqdncache_entry *t;
235
236 while (m) {
237 if (i != NULL) { /* need to delay deletion */
238 fqdncacheRelease(i); /* we just override locks */
239 i = NULL;
240 }
241
242 t = (fqdncache_entry *)m->data;
243
244 if (t->flags.fromhosts)
245 i = t;
246
247 m = m->next;
248 }
249
250 if (i != NULL)
251 fqdncacheRelease(i);
252 }
253
fqdncache_entry(const char * name)254 fqdncache_entry::fqdncache_entry(const char *name) :
255 lastref(0),
256 expires(squid_curtime + Config.negativeDnsTtl),
257 name_count(0),
258 handler(nullptr),
259 handlerData(nullptr),
260 error_message(nullptr),
261 locks(0) // XXX: use Lock
262 {
263 hash.key = xstrdup(name);
264
265 memset(&request_time, 0, sizeof(request_time));
266 memset(&names, 0, sizeof(names));
267 }
268
269 /// \ingroup FQDNCacheInternal
270 static void
fqdncacheAddEntry(fqdncache_entry * f)271 fqdncacheAddEntry(fqdncache_entry * f)
272 {
273 hash_link *e = (hash_link *)hash_lookup(fqdn_table, f->hash.key);
274
275 if (NULL != e) {
276 /* avoid colission */
277 fqdncache_entry *q = (fqdncache_entry *) e;
278 fqdncacheRelease(q);
279 }
280
281 hash_join(fqdn_table, &f->hash);
282 dlinkAdd(f, &f->lru, &lru_list);
283 f->lastref = squid_curtime;
284 }
285
286 /**
287 \ingroup FQDNCacheInternal
288 *
289 * Walks down the pending list, calling handlers
290 */
291 static void
fqdncacheCallback(fqdncache_entry * f,int wait)292 fqdncacheCallback(fqdncache_entry * f, int wait)
293 {
294 FQDNH *callback;
295 void *cbdata;
296 f->lastref = squid_curtime;
297
298 if (!f->handler)
299 return;
300
301 fqdncacheLockEntry(f);
302
303 callback = f->handler;
304
305 f->handler = NULL;
306
307 if (cbdataReferenceValidDone(f->handlerData, &cbdata)) {
308 const Dns::LookupDetails details(f->error_message, wait);
309 callback(f->name_count ? f->names[0] : NULL, details, cbdata);
310 }
311
312 fqdncacheUnlockEntry(f);
313 }
314
315 /// \ingroup FQDNCacheInternal
316 static int
fqdncacheParse(fqdncache_entry * f,const rfc1035_rr * answers,int nr,const char * error_message)317 fqdncacheParse(fqdncache_entry *f, const rfc1035_rr * answers, int nr, const char *error_message)
318 {
319 int k;
320 int ttl = 0;
321 const char *name = (const char *)f->hash.key;
322 f->expires = squid_curtime + Config.negativeDnsTtl;
323 f->flags.negcached = true;
324
325 if (nr < 0) {
326 debugs(35, 3, "fqdncacheParse: Lookup of '" << name << "' failed (" << error_message << ")");
327 f->error_message = xstrdup(error_message);
328 return -1;
329 }
330
331 if (nr == 0) {
332 debugs(35, 3, "fqdncacheParse: No DNS records for '" << name << "'");
333 f->error_message = xstrdup("No DNS records");
334 return 0;
335 }
336
337 debugs(35, 3, "fqdncacheParse: " << nr << " answers for '" << name << "'");
338 assert(answers);
339
340 for (k = 0; k < nr; ++k) {
341 if (answers[k]._class != RFC1035_CLASS_IN)
342 continue;
343
344 if (answers[k].type == RFC1035_TYPE_PTR) {
345 if (!answers[k].rdata[0]) {
346 debugs(35, 2, "fqdncacheParse: blank PTR record for '" << name << "'");
347 continue;
348 }
349
350 if (strchr(answers[k].rdata, ' ')) {
351 debugs(35, 2, "fqdncacheParse: invalid PTR record '" << answers[k].rdata << "' for '" << name << "'");
352 continue;
353 }
354
355 f->names[f->name_count] = xstrdup(answers[k].rdata);
356 ++ f->name_count;
357 } else if (answers[k].type != RFC1035_TYPE_CNAME)
358 continue;
359
360 if (ttl == 0 || (int) answers[k].ttl < ttl)
361 ttl = answers[k].ttl;
362
363 if (f->name_count >= FQDN_MAX_NAMES)
364 break;
365 }
366
367 if (f->name_count == 0) {
368 debugs(35, DBG_IMPORTANT, "fqdncacheParse: No PTR record for '" << name << "'");
369 return 0;
370 }
371
372 if (ttl > Config.positiveDnsTtl)
373 ttl = Config.positiveDnsTtl;
374
375 if (ttl < Config.negativeDnsTtl)
376 ttl = Config.negativeDnsTtl;
377
378 f->expires = squid_curtime + ttl;
379
380 f->flags.negcached = false;
381
382 return f->name_count;
383 }
384
385 /**
386 \ingroup FQDNCacheAPI
387 *
388 * Callback for handling DNS results.
389 */
390 static void
fqdncacheHandleReply(void * data,const rfc1035_rr * answers,int na,const char * error_message)391 fqdncacheHandleReply(void *data, const rfc1035_rr * answers, int na, const char *error_message)
392 {
393 fqdncache_entry *f;
394 static_cast<generic_cbdata *>(data)->unwrap(&f);
395 ++FqdncacheStats.replies;
396 const int age = f->age();
397 statCounter.dns.svcTime.count(age);
398 fqdncacheParse(f, answers, na, error_message);
399 fqdncacheAddEntry(f);
400 fqdncacheCallback(f, age);
401 }
402
403 /**
404 \ingroup FQDNCacheAPI
405 *
406 \param addr IP address of domain to resolve.
407 \param handler A pointer to the function to be called when
408 * the reply from the FQDN cache
409 * (or the DNS if the FQDN cache misses)
410 \param handlerData Information that is passed to the handler
411 * and does not affect the FQDN cache.
412 */
413 void
fqdncache_nbgethostbyaddr(const Ip::Address & addr,FQDNH * handler,void * handlerData)414 fqdncache_nbgethostbyaddr(const Ip::Address &addr, FQDNH * handler, void *handlerData)
415 {
416 fqdncache_entry *f = NULL;
417 char name[MAX_IPSTRLEN];
418 generic_cbdata *c;
419 addr.toStr(name,MAX_IPSTRLEN);
420 debugs(35, 4, "fqdncache_nbgethostbyaddr: Name '" << name << "'.");
421 ++FqdncacheStats.requests;
422
423 if (name[0] == '\0') {
424 debugs(35, 4, "fqdncache_nbgethostbyaddr: Invalid name!");
425 const Dns::LookupDetails details("Invalid hostname", -1); // error, no lookup
426 if (handler)
427 handler(NULL, details, handlerData);
428 return;
429 }
430
431 f = fqdncache_get(name);
432
433 if (NULL == f) {
434 /* miss */
435 (void) 0;
436 } else if (fqdncacheExpiredEntry(f)) {
437 /* hit, but expired -- bummer */
438 fqdncacheRelease(f);
439 f = NULL;
440 } else {
441 /* hit */
442 debugs(35, 4, "fqdncache_nbgethostbyaddr: HIT for '" << name << "'");
443
444 if (f->flags.negcached)
445 ++ FqdncacheStats.negative_hits;
446 else
447 ++ FqdncacheStats.hits;
448
449 f->handler = handler;
450
451 f->handlerData = cbdataReference(handlerData);
452
453 fqdncacheCallback(f, -1); // no lookup
454
455 return;
456 }
457
458 debugs(35, 5, "fqdncache_nbgethostbyaddr: MISS for '" << name << "'");
459 ++ FqdncacheStats.misses;
460 f = new fqdncache_entry(name);
461 f->handler = handler;
462 f->handlerData = cbdataReference(handlerData);
463 f->request_time = current_time;
464 c = new generic_cbdata(f);
465 idnsPTRLookup(addr, fqdncacheHandleReply, c);
466 }
467
468 /**
469 \ingroup FQDNCacheAPI
470 *
471 * Is different in that it only checks if an entry exists in
472 * it's data-structures and does not by default contact the
473 * DNS, unless this is requested, by setting the flags
474 * to FQDN_LOOKUP_IF_MISS.
475 *
476 \param addr address of the FQDN being resolved
477 \param flags values are NULL or FQDN_LOOKUP_IF_MISS. default is NULL.
478 *
479 */
480 const char *
fqdncache_gethostbyaddr(const Ip::Address & addr,int flags)481 fqdncache_gethostbyaddr(const Ip::Address &addr, int flags)
482 {
483 char name[MAX_IPSTRLEN];
484 fqdncache_entry *f = NULL;
485
486 if (addr.isAnyAddr() || addr.isNoAddr()) {
487 return NULL;
488 }
489
490 addr.toStr(name,MAX_IPSTRLEN);
491 ++ FqdncacheStats.requests;
492 f = fqdncache_get(name);
493
494 if (NULL == f) {
495 (void) 0;
496 } else if (fqdncacheExpiredEntry(f)) {
497 fqdncacheRelease(f);
498 f = NULL;
499 } else if (f->flags.negcached) {
500 ++ FqdncacheStats.negative_hits;
501 // ignore f->error_message: the caller just checks FQDN cache presence
502 return NULL;
503 } else {
504 ++ FqdncacheStats.hits;
505 f->lastref = squid_curtime;
506 // ignore f->error_message: the caller just checks FQDN cache presence
507 return f->names[0];
508 }
509
510 /* no entry [any more] */
511
512 ++ FqdncacheStats.misses;
513
514 if (flags & FQDN_LOOKUP_IF_MISS) {
515 fqdncache_nbgethostbyaddr(addr, NULL, NULL);
516 }
517
518 return NULL;
519 }
520
521 /**
522 \ingroup FQDNCacheInternal
523 *
524 * Process objects list
525 */
526 void
fqdnStats(StoreEntry * sentry)527 fqdnStats(StoreEntry * sentry)
528 {
529 fqdncache_entry *f = NULL;
530 int k;
531 int ttl;
532
533 if (fqdn_table == NULL)
534 return;
535
536 storeAppendPrintf(sentry, "FQDN Cache Statistics:\n");
537
538 storeAppendPrintf(sentry, "FQDNcache Entries In Use: %d\n",
539 fqdncache_entry::UseCount());
540
541 storeAppendPrintf(sentry, "FQDNcache Entries Cached: %d\n",
542 fqdncacheCount());
543
544 storeAppendPrintf(sentry, "FQDNcache Requests: %d\n",
545 FqdncacheStats.requests);
546
547 storeAppendPrintf(sentry, "FQDNcache Hits: %d\n",
548 FqdncacheStats.hits);
549
550 storeAppendPrintf(sentry, "FQDNcache Negative Hits: %d\n",
551 FqdncacheStats.negative_hits);
552
553 storeAppendPrintf(sentry, "FQDNcache Misses: %d\n",
554 FqdncacheStats.misses);
555
556 storeAppendPrintf(sentry, "FQDN Cache Contents:\n\n");
557
558 storeAppendPrintf(sentry, "%-45.45s %3s %3s %3s %s\n",
559 "Address", "Flg", "TTL", "Cnt", "Hostnames");
560
561 hash_first(fqdn_table);
562
563 while ((f = (fqdncache_entry *) hash_next(fqdn_table))) {
564 ttl = (f->flags.fromhosts ? -1 : (f->expires - squid_curtime));
565 storeAppendPrintf(sentry, "%-45.45s %c%c %3.3d % 3d",
566 hashKeyStr(&f->hash),
567 f->flags.negcached ? 'N' : ' ',
568 f->flags.fromhosts ? 'H' : ' ',
569 ttl,
570 (int) f->name_count);
571
572 for (k = 0; k < (int) f->name_count; ++k)
573 storeAppendPrintf(sentry, " %s", f->names[k]);
574
575 storeAppendPrintf(sentry, "\n");
576 }
577 }
578
579 /// \ingroup FQDNCacheInternal
580 static void
fqdncacheLockEntry(fqdncache_entry * f)581 fqdncacheLockEntry(fqdncache_entry * f)
582 {
583 if (f->locks++ == 0) {
584 dlinkDelete(&f->lru, &lru_list);
585 dlinkAdd(f, &f->lru, &lru_list);
586 }
587 }
588
589 /// \ingroup FQDNCacheInternal
590 static void
fqdncacheUnlockEntry(fqdncache_entry * f)591 fqdncacheUnlockEntry(fqdncache_entry * f)
592 {
593 assert(f->locks > 0);
594 -- f->locks;
595
596 if (fqdncacheExpiredEntry(f))
597 fqdncacheRelease(f);
598 }
599
600 /// \ingroup FQDNCacheInternal
601 static void
fqdncacheFreeEntry(void * data)602 fqdncacheFreeEntry(void *data)
603 {
604 fqdncache_entry *f = (fqdncache_entry *)data;
605 delete f;
606 }
607
~fqdncache_entry()608 fqdncache_entry::~fqdncache_entry()
609 {
610 for (int k = 0; k < (int)name_count; ++k)
611 xfree(names[k]);
612
613 xfree(hash.key);
614 xfree(error_message);
615 }
616
617 /// \ingroup FQDNCacheAPI
618 void
fqdncacheFreeMemory(void)619 fqdncacheFreeMemory(void)
620 {
621 hashFreeItems(fqdn_table, fqdncacheFreeEntry);
622 hashFreeMemory(fqdn_table);
623 fqdn_table = NULL;
624 }
625
626 /**
627 \ingroup FQDNCacheAPI
628 *
629 * Recalculate FQDN cache size upon reconfigure.
630 * Is called to clear the FQDN cache's data structures,
631 * cancel all pending requests.
632 */
633 void
fqdncache_restart(void)634 fqdncache_restart(void)
635 {
636 fqdncache_high = (long) (((float) Config.fqdncache.size *
637 (float) FQDN_HIGH_WATER) / (float) 100);
638 fqdncache_low = (long) (((float) Config.fqdncache.size *
639 (float) FQDN_LOW_WATER) / (float) 100);
640 purge_entries_fromhosts();
641 }
642
643 /**
644 * Adds a "static" entry from /etc/hosts.
645 *
646 \param addr FQDN name to be added.
647 \param hostnames list of hostnames for the addr
648 */
649 void
fqdncacheAddEntryFromHosts(char * addr,SBufList & hostnames)650 fqdncacheAddEntryFromHosts(char *addr, SBufList &hostnames)
651 {
652 fqdncache_entry *fce= fqdncache_get(addr);
653 if (fce) {
654 if (1 == fce->flags.fromhosts) {
655 fqdncacheUnlockEntry(fce);
656 } else if (fce->locks > 0) {
657 debugs(35, DBG_IMPORTANT, "WARNING: can't add static entry for locked address '" << addr << "'");
658 return;
659 } else {
660 fqdncacheRelease(fce);
661 }
662 }
663
664 fce = new fqdncache_entry(addr);
665
666 int j = 0;
667 for (auto &h : hostnames) {
668 fce->names[j] = xstrdup(h.c_str());
669 Tolower(fce->names[j]);
670 ++j;
671
672 if (j >= FQDN_MAX_NAMES)
673 break;
674 }
675
676 fce->name_count = j;
677 fce->names[j] = NULL; /* it's safe */
678 fce->flags.fromhosts = true;
679 fqdncacheAddEntry(fce);
680 fqdncacheLockEntry(fce);
681 }
682
683 /// \ingroup FQDNCacheInternal
684 static void
fqdncacheRegisterWithCacheManager(void)685 fqdncacheRegisterWithCacheManager(void)
686 {
687 Mgr::RegisterAction("fqdncache", "FQDN Cache Stats and Contents",
688 fqdnStats, 0, 1);
689
690 }
691
692 /**
693 \ingroup FQDNCacheAPI
694 *
695 * Initialize the fqdncache.
696 * Called after IP cache initialization.
697 */
698 void
fqdncache_init(void)699 fqdncache_init(void)
700 {
701 int n;
702
703 fqdncacheRegisterWithCacheManager();
704
705 if (fqdn_table)
706 return;
707
708 debugs(35, 3, "Initializing FQDN Cache...");
709
710 memset(&FqdncacheStats, '\0', sizeof(FqdncacheStats));
711 lru_list = dlink_list();
712
713 fqdncache_high = (long) (((float) Config.fqdncache.size *
714 (float) FQDN_HIGH_WATER) / (float) 100);
715
716 fqdncache_low = (long) (((float) Config.fqdncache.size *
717 (float) FQDN_LOW_WATER) / (float) 100);
718
719 n = hashPrime(fqdncache_high / 4);
720
721 fqdn_table = hash_create((HASHCMP *) strcmp, n, hash4);
722 }
723
724 #if SQUID_SNMP
725 /**
726 * \ingroup FQDNCacheAPI
727 * The function to return the FQDN statistics via SNMP
728 */
729 variable_list *
snmp_netFqdnFn(variable_list * Var,snint * ErrP)730 snmp_netFqdnFn(variable_list * Var, snint * ErrP)
731 {
732 variable_list *Answer = NULL;
733 MemBuf tmp;
734 debugs(49, 5, "snmp_netFqdnFn: Processing request:" << snmpDebugOid(Var->name, Var->name_length, tmp));
735 *ErrP = SNMP_ERR_NOERROR;
736
737 switch (Var->name[LEN_SQ_NET + 1]) {
738
739 case FQDN_ENT:
740 Answer = snmp_var_new_integer(Var->name, Var->name_length,
741 fqdncacheCount(),
742 SMI_GAUGE32);
743 break;
744
745 case FQDN_REQ:
746 Answer = snmp_var_new_integer(Var->name, Var->name_length,
747 FqdncacheStats.requests,
748 SMI_COUNTER32);
749 break;
750
751 case FQDN_HITS:
752 Answer = snmp_var_new_integer(Var->name, Var->name_length,
753 FqdncacheStats.hits,
754 SMI_COUNTER32);
755 break;
756
757 case FQDN_PENDHIT:
758 /* this is now worthless */
759 Answer = snmp_var_new_integer(Var->name, Var->name_length,
760 0,
761 SMI_GAUGE32);
762 break;
763
764 case FQDN_NEGHIT:
765 Answer = snmp_var_new_integer(Var->name, Var->name_length,
766 FqdncacheStats.negative_hits,
767 SMI_COUNTER32);
768 break;
769
770 case FQDN_MISS:
771 Answer = snmp_var_new_integer(Var->name, Var->name_length,
772 FqdncacheStats.misses,
773 SMI_COUNTER32);
774 break;
775
776 case FQDN_GHBN:
777 Answer = snmp_var_new_integer(Var->name, Var->name_length,
778 0, /* deprecated */
779 SMI_COUNTER32);
780 break;
781
782 default:
783 *ErrP = SNMP_ERR_NOSUCHNAME;
784 break;
785 }
786
787 return Answer;
788 }
789
790 #endif /*SQUID_SNMP */
791
792