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