]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ipcache.cc
Docs: Copyright updates for 2018 (#114)
[thirdparty/squid.git] / src / ipcache.cc
CommitLineData
30a4f2a8 1/*
5b74111a 2 * Copyright (C) 1996-2018 The Squid Software Foundation and contributors
e25c139f 3 *
bbc27441
AJ
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.
019dd986 7 */
44a47c6e 8
bbc27441
AJ
9/* DEBUG: section 14 IP Cache */
10
582c2af2 11#include "squid.h"
62ee09ca 12#include "CacheManager.h"
602d9612 13#include "cbdata.h"
17852883 14#include "dlink.h"
4a3b98d7
AJ
15#include "dns/LookupDetails.h"
16#include "dns/rfc3596.h"
cfd66529
AJ
17#include "event.h"
18#include "ip/Address.h"
055421ee 19#include "ip/tools.h"
714e68b7 20#include "ipcache.h"
8822ebee 21#include "mgr/Registration.h"
4d5904f7 22#include "SquidConfig.h"
985c86bc 23#include "SquidTime.h"
e4f1fdae 24#include "StatCounters.h"
e6ccf245 25#include "Store.h"
ed6e9fb9 26#include "util.h"
d295d770 27#include "wordlist.h"
44a47c6e 28
9c0a2256
FC
29#if SQUID_SNMP
30#include "snmp_core.h"
31#endif
32
63be0a78 33/**
34 \defgroup IPCacheAPI IP Cache API
35 \ingroup Components
36 \section Introduction Introduction
37 \par
38 * The IP cache is a built-in component of squid providing
39 * Hostname to IP-Number translation functionality and managing
40 * the involved data-structures. Efficiency concerns require
41 * mechanisms that allow non-blocking access to these mappings.
42 * The IP cache usually doesn't block on a request except for
43 * special cases where this is desired (see below).
44 *
45 \todo IP Cache should have its own API *.h header file.
46 */
47
48/**
49 \defgroup IPCacheInternal IP Cache Internals
50 \ingroup IPCacheAPI
51 \todo when IP cache is provided as a class. These sub-groups will be obsolete
2b61af8e 52 * for now they are used to separate the public and private functions.
f53969cc 53 * with the private ones all being in IPCachInternal and public in IPCacheAPI
63be0a78 54 *
55 \section InternalOperation Internal Operation
56 *
57 * Internally, the execution flow is as follows: On a miss,
58 * ipcache_getnbhostbyname checks whether a request for
59 * this name is already pending, and if positive, it creates
60 * a new entry using ipcacheAddNew with the IP_PENDING
61 * flag set . Then it calls ipcacheAddPending to add a
62 * request to the queue together with data and handler. Else,
63 * ipcache_dnsDispatch() is called to directly create a
64 * DNS query or to ipcacheEnqueue() if all no DNS port
65 * is free. ipcache_call_pending() is called regularly
66 * to walk down the pending list and call handlers. LRU clean-up
67 * is performed through ipcache_purgelru() according to
68 * the ipcache_high threshold.
69 */
70
fd9c47d1
AR
71/// metadata for parsing DNS A and AAAA records
72template <class Content>
73class RrSpecs
74{
75public:
76 typedef Content DataType; ///< actual RR DATA type
77 const char *kind; ///< human-friendly record type description
78 int &recordCounter; ///< where this kind of records are counted (for stats)
79};
80
81/// forwards non-blocking IP cache lookup results to either IPH or IpReciever
82class IpCacheLookupForwarder
83{
84public:
85 IpCacheLookupForwarder() {}
86 explicit IpCacheLookupForwarder(const CbcPointer<Dns::IpReceiver> &receiver);
87 IpCacheLookupForwarder(IPH *fun, void *data);
88
89 /// forwards notification about the end of the lookup; last method to be called
90 void finalCallback(const Dns::CachedIps *addrs, const Dns::LookupDetails &details);
91
92 /// forwards an IP notification
93 /// \returns whether it may be possible to deliver more notifications
94 bool forwardIp(const Ip::Address &ip);
95
96 /// convenience wrapper to safely forwardIp() for each IP in the container
97 void forwardHits(const Dns::CachedIps &ips);
98
99 /// initialize lookup timestamps for Dns::LookupDetails delay calculation
100 void lookupsStarting() { firstLookupStart = lastLookupEnd = current_time; }
101
102 /// inform recipient of a finished lookup
103 void forwardLookup(const char *error);
104
105 /// \returns milliseconds since the first lookup start
106 int totalResponseTime() const { return tvSubMsec(firstLookupStart, current_time); }
107
108protected:
109 /// \returns not yet reported lookup delay in milliseconds
110 int additionalLookupDelay() const { return tvSubMsec(lastLookupEnd, current_time); }
111
112private:
113 /* receiverObj and receiverFun are mutually exclusive */
114 CbcPointer<Dns::IpReceiver> receiverObj; ///< gets incremental and final results
115 IPH *receiverFun = nullptr; ///< gets final results
116 CallbackData receiverData; ///< caller-specific data for the handler (optional)
117
118 struct timeval firstLookupStart {0,0}; ///< time of the idnsALookup() call
119 struct timeval lastLookupEnd {0,0}; ///< time of the last noteLookup() call
120};
121
63be0a78 122/**
123 \ingroup IPCacheAPI
124 *
125 * The data structure used for storing name-address mappings
126 * is a small hashtable (static hash_table *ip_table),
127 * where structures of type ipcache_entry whose most
128 * interesting members are:
129 */
e1381638
AJ
130class ipcache_entry
131{
fd9c47d1 132 CBDATA_CLASS(ipcache_entry);
3c670b50 133
3ff65596 134public:
3c670b50
AJ
135 ipcache_entry(const char *);
136 ~ipcache_entry();
137
f53969cc 138 hash_link hash; /* must be first */
ecc3091b 139 time_t lastref;
140 time_t expires;
141 ipcache_addrs addrs;
fd9c47d1 142 IpCacheLookupForwarder handler;
ecc3091b 143 char *error_message;
62e76326 144
ecc3091b 145 dlink_node lru;
ac06f720 146 unsigned short locks;
3c670b50
AJ
147 struct Flags {
148 Flags() : negcached(false), fromhosts(false) {}
149
be4d35dc
FC
150 bool negcached;
151 bool fromhosts;
2fadd50d 152 } flags;
3ff65596 153
fd9c47d1
AR
154 bool sawCname = false;
155
156 const char *name() const { return static_cast<const char*>(hash.key); }
157
158 /// milliseconds since the first lookup start or -1 if there were no lookups
159 int totalResponseTime() const;
160 /// milliseconds since the last lookup start or -1 if there were no lookups
161 int additionalLookupDelay() const;
162
163 /// adds the contents of a "good" DNS A or AAAA record to stored IPs
164 template <class Specs>
165 void addGood(const rfc1035_rr &rr, Specs &specs);
166
167 /// remembers the last error seen, overwriting any previous errors
168 void latestError(const char *text, const int debugLevel = 3);
169
170protected:
171 void updateTtl(const unsigned int rrTtl);
ecc3091b 172};
173
63be0a78 174/// \ingroup IPCacheInternal
26ac0430 175static struct _ipcache_stats {
30a4f2a8 176 int requests;
f88bb09c 177 int replies;
30a4f2a8 178 int hits;
179 int misses;
30a4f2a8 180 int negative_hits;
22b245f8 181 int numeric_hits;
bae9832d 182 int rr_a;
183 int rr_aaaa;
184 int rr_cname;
185 int cname_only;
22b245f8 186 int invalid;
2fadd50d 187} IpcacheStats;
090089c4 188
63be0a78 189/// \ingroup IPCacheInternal
ce75f381 190static dlink_list lru_list;
7b04dad5 191
59a09b98
FC
192// forward-decls
193static void stat_ipcache_get(StoreEntry *);
194
74addf6c 195static FREE ipcacheFreeEntry;
7b724b86 196static IDNSCB ipcacheHandleReply;
74addf6c 197static int ipcacheExpiredEntry(ipcache_entry *);
f5b8bbc4 198static ipcache_entry *ipcache_get(const char *);
74addf6c 199static void ipcacheLockEntry(ipcache_entry *);
f5b8bbc4 200static void ipcacheStatPrint(ipcache_entry *, StoreEntry *);
201static void ipcacheUnlockEntry(ipcache_entry *);
cc192b50 202static void ipcacheRelease(ipcache_entry *, bool dofree = true);
fd9c47d1
AR
203static const Dns::CachedIps *ipcacheCheckNumeric(const char *name);
204static void ipcache_nbgethostbyname_(const char *name, IpCacheLookupForwarder handler);
30a4f2a8 205
63be0a78 206/// \ingroup IPCacheInternal
365e5b34 207static hash_table *ip_table = NULL;
090089c4 208
63be0a78 209/// \ingroup IPCacheInternal
24382924 210static long ipcache_low = 180;
63be0a78 211/// \ingroup IPCacheInternal
24382924 212static long ipcache_high = 200;
f88bb09c 213
c021888f 214#if LIBRESOLV_DNS_TTL_HACK
215extern int _dns_ttl_;
216#endif
217
fd9c47d1 218CBDATA_CLASS_INIT(ipcache_entry);
ac49890a 219
fd9c47d1
AR
220IpCacheLookupForwarder::IpCacheLookupForwarder(const CbcPointer<Dns::IpReceiver> &receiver):
221 receiverObj(receiver)
222{
223}
224
225IpCacheLookupForwarder::IpCacheLookupForwarder(IPH *fun, void *data):
226 receiverFun(fun), receiverData(data)
3ff65596 227{
3ff65596
AR
228}
229
fd9c47d1
AR
230void
231IpCacheLookupForwarder::finalCallback(const Dns::CachedIps *addrs, const Dns::LookupDetails &details)
232{
233 debugs(14, 7, addrs << " " << details);
234 if (receiverObj.set()) {
235 if (auto receiver = receiverObj.valid())
236 receiver->noteIps(addrs, details);
237 receiverObj.clear();
238 } else if (receiverFun) {
239 if (receiverData.valid()) {
240 const Dns::CachedIps *emptyIsNil = (addrs && !addrs->empty()) ? addrs : nullptr;
241 receiverFun(emptyIsNil, details, receiverData.validDone());
242 }
243 receiverFun = nullptr;
244 }
245}
246
247/// forwards an IP notification
248/// \returns whether it may be possible to deliver more notifications
249bool
250IpCacheLookupForwarder::forwardIp(const Ip::Address &ip)
251{
252 debugs(14, 7, ip);
253 if (receiverObj.set()) {
254 if (auto receiver = receiverObj.valid()) {
255 receiver->noteIp(ip);
256 return true;
257 }
258 return false;
259 }
260 // else do nothing: ReceiverFun does not do incremental notifications
261 return false;
262}
263
264/// convenience wrapper to safely forwardIp() for each IP in the container
265void
266IpCacheLookupForwarder::forwardHits(const Dns::CachedIps &ips)
267{
268 if (receiverObj.set()) {
269 for (const auto &ip: ips.good()) {
270 if (!forwardIp(ip))
271 break; // receiver gone
272 }
273 }
274 // else do nothing: ReceiverFun does not do incremental notifications
275}
276
277void
278IpCacheLookupForwarder::forwardLookup(const char *error)
279{
280 // Lookups run concurrently, but HttpRequest::recordLookup() thinks they
281 // are sequential. Give it just the new, yet-unaccounted-for delay.
282 if (receiverObj.set()) {
283 if (auto receiver = receiverObj.valid()) {
284 receiver->noteLookup(Dns::LookupDetails(error, additionalLookupDelay()));
285 lastLookupEnd = current_time;
286 }
287 }
288 // else do nothing: ReceiverFun gets no individual lookup notifications
289}
290
291/// \ingroup IPCacheInternal
292inline int ipcacheCount() { return ip_table ? ip_table->count : 0; }
293
63be0a78 294/**
295 \ingroup IPCacheInternal
296 *
297 * removes the given ipcache entry
298 */
b8d8561b 299static void
cc192b50 300ipcacheRelease(ipcache_entry * i, bool dofree)
090089c4 301{
26ac0430 302 if (!i) {
fa84c01d 303 debugs(14, DBG_CRITICAL, "ipcacheRelease: Releasing entry with i=<NULL>");
cc192b50 304 return;
305 }
306
26ac0430 307 if (!i || !i->hash.key) {
fa84c01d 308 debugs(14, DBG_CRITICAL, "ipcacheRelease: Releasing entry without hash link!");
cc192b50 309 return;
310 }
311
bf8fe701 312 debugs(14, 3, "ipcacheRelease: Releasing entry for '" << (const char *) i->hash.key << "'");
313
ecc3091b 314 hash_remove_link(ip_table, (hash_link *) i);
7b04dad5 315 dlinkDelete(&i->lru, &lru_list);
26ac0430 316 if (dofree)
cc192b50 317 ipcacheFreeEntry(i);
090089c4 318}
319
63be0a78 320/// \ingroup IPCacheInternal
b8d8561b 321static ipcache_entry *
0ee4272b 322ipcache_get(const char *name)
090089c4 323{
ecc3091b 324 if (ip_table != NULL)
62e76326 325 return (ipcache_entry *) hash_lookup(ip_table, name);
ecc3091b 326 else
62e76326 327 return NULL;
090089c4 328}
329
63be0a78 330/// \ingroup IPCacheInternal
b8d8561b 331static int
332ipcacheExpiredEntry(ipcache_entry * i)
30a4f2a8 333{
0e70aa1e 334 /* all static entries are locked, so this takes care of them too */
62e76326 335
620da955 336 if (i->locks != 0)
62e76326 337 return 0;
338
fd9c47d1 339 if (i->addrs.empty())
62e76326 340 if (0 == i->flags.negcached)
341 return 1;
342
af00901c 343 if (i->expires > squid_curtime)
62e76326 344 return 0;
345
30a4f2a8 346 return 1;
347}
090089c4 348
63be0a78 349/// \ingroup IPCacheAPI
7b04dad5 350void
ced8def3 351ipcache_purgelru(void *)
7b04dad5 352{
353 dlink_node *m;
354 dlink_node *prev = NULL;
355 ipcache_entry *i;
356 int removed = 0;
52040193 357 eventAdd("ipcache_purgelru", ipcache_purgelru, NULL, 10.0, 1);
62e76326 358
7b04dad5 359 for (m = lru_list.tail; m; m = prev) {
ac49890a 360 if (ipcacheCount() < ipcache_low)
62e76326 361 break;
362
363 prev = m->prev;
364
365 i = (ipcache_entry *)m->data;
366
367 if (i->locks != 0)
368 continue;
369
370 ipcacheRelease(i);
371
95dc7ff4 372 ++removed;
7b04dad5 373 }
62e76326 374
bf8fe701 375 debugs(14, 9, "ipcache_purgelru: removed " << removed << " entries");
7b04dad5 376}
377
63be0a78 378/**
379 \ingroup IPCacheInternal
380 *
381 * purges entries added from /etc/hosts (or whatever).
382 */
0e70aa1e 383static void
384purge_entries_fromhosts(void)
385{
386 dlink_node *m = lru_list.head;
387 ipcache_entry *i = NULL, *t;
62e76326 388
0e70aa1e 389 while (m) {
f53969cc
SM
390 if (i != NULL) { /* need to delay deletion */
391 ipcacheRelease(i); /* we just override locks */
62e76326 392 i = NULL;
393 }
394
395 t = (ipcache_entry*)m->data;
396
397 if (t->flags.fromhosts)
398 i = t;
399
400 m = m->next;
0e70aa1e 401 }
62e76326 402
0e70aa1e 403 if (i != NULL)
62e76326 404 ipcacheRelease(i);
0e70aa1e 405}
406
494269cd 407ipcache_entry::ipcache_entry(const char *aName):
3c670b50
AJ
408 lastref(0),
409 expires(0),
3c670b50
AJ
410 error_message(nullptr),
411 locks(0) // XXX: use Lock type ?
090089c4 412{
494269cd 413 hash.key = xstrdup(aName);
3c670b50
AJ
414 Tolower(static_cast<char*>(hash.key));
415 expires = squid_curtime + Config.negativeDnsTtl;
090089c4 416}
417
63be0a78 418/// \ingroup IPCacheInternal
b8d8561b 419static void
ecc3091b 420ipcacheAddEntry(ipcache_entry * i)
dd7ad0a4 421{
e6ccf245 422 hash_link *e = (hash_link *)hash_lookup(ip_table, i->hash.key);
62e76326 423
ecc3091b 424 if (NULL != e) {
62e76326 425 /* avoid colission */
426 ipcache_entry *q = (ipcache_entry *) e;
2b72648d 427 ipcacheRelease(q);
ecc3091b 428 }
62e76326 429
186477c1 430 hash_join(ip_table, &i->hash);
ecc3091b 431 dlinkAdd(i, &i->lru, &lru_list);
dd7ad0a4 432 i->lastref = squid_curtime;
090089c4 433}
434
63be0a78 435/**
436 \ingroup IPCacheInternal
437 *
438 * walks down the pending list, calling handlers
439 */
b8d8561b 440static void
fd9c47d1 441ipcacheCallback(ipcache_entry *i, const bool hit, const int wait)
090089c4 442{
30a4f2a8 443 i->lastref = squid_curtime;
62e76326 444
fa80a8ef 445 ipcacheLockEntry(i);
62e76326 446
fd9c47d1
AR
447 if (hit)
448 i->handler.forwardHits(i->addrs);
449 const Dns::LookupDetails details(i->error_message, wait);
450 i->handler.finalCallback(&i->addrs, details);
62e76326 451
620da955 452 ipcacheUnlockEntry(i);
090089c4 453}
454
fd9c47d1
AR
455void
456ipcache_entry::latestError(const char *text, const int debugLevel)
457{
458 debugs(14, debugLevel, "DNS error while resolving " << name() << ": " << text);
459 safe_free(error_message);
460 error_message = xstrdup(text);
461}
462
7eb648e2 463static void
33ab4aaf 464ipcacheParse(ipcache_entry *i, const rfc1035_rr * answers, int nr, const char *error_message)
7b724b86 465{
7cfc1c9a 466 int k;
62e76326 467
fd9c47d1 468 // XXX: Callers use zero ancount instead of -1 on errors!
7cfc1c9a 469 if (nr < 0) {
fd9c47d1 470 i->latestError(error_message);
7eb648e2 471 return;
7cfc1c9a 472 }
62e76326 473
7cfc1c9a 474 if (nr == 0) {
fd9c47d1 475 i->latestError("No DNS records");
7eb648e2 476 return;
7cfc1c9a 477 }
62e76326 478
fd9c47d1 479 debugs(14, 3, nr << " answers for " << i->name());
7cfc1c9a 480 assert(answers);
62e76326 481
95dc7ff4 482 for (k = 0; k < nr; ++k) {
62e76326 483
055421ee 484 if (Ip::EnableIpv6 && answers[k].type == RFC1035_TYPE_AAAA) {
fd9c47d1
AR
485 static const RrSpecs<struct in6_addr> QuadA = { "IPv6", IpcacheStats.rr_aaaa };
486 i->addGood(answers[k], QuadA);
26ac0430
AJ
487 continue;
488 }
62e76326 489
cc192b50 490 if (answers[k].type == RFC1035_TYPE_A) {
fd9c47d1
AR
491 static const RrSpecs<struct in_addr> SingleA = { "IPv4", IpcacheStats.rr_a };
492 i->addGood(answers[k], SingleA);
26ac0430
AJ
493 continue;
494 }
cc192b50 495
26ac0430 496 /* With A and AAAA, the CNAME does not necessarily come with additional records to use. */
cc192b50 497 if (answers[k].type == RFC1035_TYPE_CNAME) {
fd9c47d1 498 i->sawCname = true;
95dc7ff4 499 ++IpcacheStats.rr_cname;
470cd749 500 continue;
501 }
bae9832d 502
503 // otherwise its an unknown RR. debug at level 9 since we usually want to ignore these and they are common.
7eb648e2 504 debugs(14, 9, "Unknown RR type received: type=" << answers[k].type << " starting at " << &(answers[k]) );
cc192b50 505 }
fd9c47d1
AR
506}
507
508template <class Specs>
509void
510ipcache_entry::addGood(const rfc1035_rr &rr, Specs &specs)
511{
512 typename Specs::DataType address;
513 if (rr.rdlength != sizeof(address)) {
514 debugs(14, DBG_IMPORTANT, "ERROR: Ignoring invalid " << specs.kind << " address record while resolving " << name());
7eb648e2 515 return;
7cfc1c9a 516 }
62e76326 517
fd9c47d1 518 ++specs.recordCounter;
cc192b50 519
fd9c47d1
AR
520 // Do not store more than 255 addresses (TODO: Why?)
521 if (addrs.raw().size() >= 255)
522 return;
cc192b50 523
fd9c47d1
AR
524 memcpy(&address, rr.rdata, sizeof(address));
525 const Ip::Address ip = address;
526 if (addrs.have(ip)) {
527 debugs(14, 3, "refusing to add duplicate " << ip);
528 return;
7b724b86 529 }
fd9c47d1 530 addrs.pushUnique(address);
62e76326 531
fd9c47d1 532 updateTtl(rr.ttl);
7ba8d0b8 533
fd9c47d1
AR
534 debugs(14, 3, name() << " #" << addrs.size() << " " << ip);
535 handler.forwardIp(ip); // we are only called with good IPs
536}
7ba8d0b8 537
fd9c47d1
AR
538void
539ipcache_entry::updateTtl(const unsigned int rrTtl)
540{
541 const time_t ttl = std::min(std::max(
542 Config.negativeDnsTtl, // smallest value allowed
543 static_cast<time_t>(rrTtl)),
544 Config.positiveDnsTtl); // largest value allowed
545
546 const time_t rrExpires = squid_curtime + ttl;
547 if (rrExpires < expires)
548 expires = rrExpires;
7b724b86 549}
62e76326 550
63be0a78 551/// \ingroup IPCacheInternal
a7e59001 552static void
fd9c47d1 553ipcacheHandleReply(void *data, const rfc1035_rr * answers, int na, const char *error_message, const bool lastAnswer)
090089c4 554{
fd9c47d1
AR
555 ipcache_entry *i = static_cast<ipcache_entry*>(data);
556
557 i->handler.forwardLookup(error_message);
558 ipcacheParse(i, answers, na, error_message);
559
560 if (!lastAnswer)
561 return;
562
95dc7ff4 563 ++IpcacheStats.replies;
fd9c47d1 564 const auto age = i->handler.totalResponseTime();
e8baef82 565 statCounter.dns.svcTime.count(age);
e1381638 566
fd9c47d1
AR
567 if (i->addrs.empty()) {
568 i->flags.negcached = true;
569 i->expires = squid_curtime + Config.negativeDnsTtl;
570
571 if (!i->error_message) {
572 i->latestError("No valid address records", DBG_IMPORTANT);
573 if (i->sawCname)
574 ++IpcacheStats.cname_only;
575 }
576 }
577
578 debugs(14, 3, "done with " << i->name() << ": " << i->addrs);
7eb648e2 579 ipcacheAddEntry(i);
fd9c47d1 580 ipcacheCallback(i, false, age);
30a4f2a8 581}
582
63be0a78 583/**
584 \ingroup IPCacheAPI
585 *
f53969cc
SM
586 \param name Host to resolve.
587 \param handler Pointer to the function to be called when the reply
588 * from the IP cache (or the DNS if the IP cache misses)
589 \param handlerData Information that is passed to the handler and does not affect the IP cache.
79300bcb
AR
590 *
591 * XXX: on hits and some errors, the handler is called immediately instead
592 * of scheduling an async call. This reentrant behavior means that the
593 * user job must be extra careful after calling ipcache_nbgethostbyname,
26ac0430 594 * especially if the handler destroys the job. Moreover, the job has
55cbb02b
AJ
595 * no way of knowing whether the reentrant call happened.
596 * Comm::Connection setup usually protects the job by scheduling an async call,
597 * but some user code calls ipcache_nbgethostbyname directly.
63be0a78 598 */
b8d8561b 599void
8407afee 600ipcache_nbgethostbyname(const char *name, IPH * handler, void *handlerData)
fd9c47d1
AR
601{
602 debugs(14, 4, name);
603 ipcache_nbgethostbyname_(name, IpCacheLookupForwarder(handler, handlerData));
604}
605
606void
607Dns::nbgethostbyname(const char *name, const CbcPointer<IpReceiver> &receiver)
608{
609 debugs(14, 4, name);
610 ipcache_nbgethostbyname_(name, IpCacheLookupForwarder(receiver));
611}
612
613/// implements ipcache_nbgethostbyname() and Dns::nbgethostbyname() APIs
614static void
615ipcache_nbgethostbyname_(const char *name, IpCacheLookupForwarder handler)
090089c4 616{
30a4f2a8 617 ipcache_entry *i = NULL;
429fdbec 618 const ipcache_addrs *addrs = NULL;
95dc7ff4 619 ++IpcacheStats.requests;
62e76326 620
090089c4 621 if (name == NULL || name[0] == '\0') {
bf8fe701 622 debugs(14, 4, "ipcache_nbgethostbyname: Invalid name!");
95dc7ff4 623 ++IpcacheStats.invalid;
4a3b98d7 624 const Dns::LookupDetails details("Invalid hostname", -1); // error, no lookup
fd9c47d1 625 handler.finalCallback(nullptr, details);
62e76326 626 return;
af00901c 627 }
62e76326 628
e5f6c5c2 629 if ((addrs = ipcacheCheckNumeric(name))) {
cc192b50 630 debugs(14, 4, "ipcache_nbgethostbyname: BYPASS for '" << name << "' (already numeric)");
8e44d1a4 631 handler.forwardHits(*addrs);
95dc7ff4 632 ++IpcacheStats.numeric_hits;
4a3b98d7 633 const Dns::LookupDetails details; // no error, no lookup
fd9c47d1 634 handler.finalCallback(addrs, details);
62e76326 635 return;
090089c4 636 }
62e76326 637
ecc3091b 638 i = ipcache_get(name);
62e76326 639
ecc3091b 640 if (NULL == i) {
62e76326 641 /* miss */
642 (void) 0;
ecc3091b 643 } else if (ipcacheExpiredEntry(i)) {
62e76326 644 /* hit, but expired -- bummer */
645 ipcacheRelease(i);
646 i = NULL;
ecc3091b 647 } else {
62e76326 648 /* hit */
bf8fe701 649 debugs(14, 4, "ipcache_nbgethostbyname: HIT for '" << name << "'");
62e76326 650
651 if (i->flags.negcached)
95dc7ff4 652 ++IpcacheStats.negative_hits;
62e76326 653 else
95dc7ff4 654 ++IpcacheStats.hits;
62e76326 655
fd9c47d1
AR
656 i->handler = std::move(handler);
657 ipcacheCallback(i, true, -1); // no lookup
62e76326 658
659 return;
090089c4 660 }
62e76326 661
bf8fe701 662 debugs(14, 5, "ipcache_nbgethostbyname: MISS for '" << name << "'");
95dc7ff4 663 ++IpcacheStats.misses;
3c670b50 664 i = new ipcache_entry(name);
fd9c47d1
AR
665 i->handler = std::move(handler);
666 i->handler.lookupsStarting();
667 idnsALookup(hashKeyStr(&i->hash), ipcacheHandleReply, i);
090089c4 668}
669
5f5e883f
FC
670/// \ingroup IPCacheInternal
671static void
672ipcacheRegisterWithCacheManager(void)
673{
8822ebee 674 Mgr::RegisterAction("ipcache",
d9fc6862
A
675 "IP Cache Stats and Contents",
676 stat_ipcache_get, 0, 1);
5f5e883f
FC
677}
678
63be0a78 679/**
680 \ingroup IPCacheAPI
681 *
682 * Initialize the ipcache.
683 * Is called from mainInitialize() after disk initialization
684 * and prior to the reverse FQDNCache initialization
685 */
b8d8561b 686void
0673c0ba 687ipcache_init(void)
0ffd22bc 688{
aa9e2cab 689 int n;
8eb28163 690 debugs(14, DBG_IMPORTANT, "Initializing IP Cache...");
30a4f2a8 691 memset(&IpcacheStats, '\0', sizeof(IpcacheStats));
3eb55834 692 memset(&lru_list, '\0', sizeof(lru_list));
62e76326 693
b15e6857 694 ipcache_high = (long) (((float) Config.ipcache.size *
62e76326 695 (float) Config.ipcache.high) / (float) 100);
b15e6857 696 ipcache_low = (long) (((float) Config.ipcache.size *
62e76326 697 (float) Config.ipcache.low) / (float) 100);
aa9e2cab 698 n = hashPrime(ipcache_high / 4);
30abd221 699 ip_table = hash_create((HASHCMP *) strcmp, n, hash4);
d120ed12
FC
700
701 ipcacheRegisterWithCacheManager();
090089c4 702}
703
63be0a78 704/**
705 \ingroup IPCacheAPI
706 *
707 * Is different from ipcache_nbgethostbyname in that it only checks
708 * if an entry exists in the cache and does not by default contact the DNS,
709 * unless this is requested, by setting the flags.
710 *
f53969cc
SM
711 \param name Host name to resolve.
712 \param flags Default is NULL, set to IP_LOOKUP_IF_MISS
713 * to explicitly perform DNS lookups.
63be0a78 714 *
f53969cc
SM
715 \retval NULL An error occured during lookup
716 \retval NULL No results available in cache and no lookup specified
717 \retval * Pointer to the ipcahce_addrs structure containing the lookup results
63be0a78 718 */
0ee4272b 719const ipcache_addrs *
720ipcache_gethostbyname(const char *name, int flags)
090089c4 721{
30a4f2a8 722 ipcache_entry *i = NULL;
ecc3091b 723 assert(name);
bf8fe701 724 debugs(14, 3, "ipcache_gethostbyname: '" << name << "', flags=" << std::hex << flags);
95dc7ff4 725 ++IpcacheStats.requests;
ecc3091b 726 i = ipcache_get(name);
62e76326 727
ecc3091b 728 if (NULL == i) {
62e76326 729 (void) 0;
ecc3091b 730 } else if (ipcacheExpiredEntry(i)) {
62e76326 731 ipcacheRelease(i);
732 i = NULL;
ecc3091b 733 } else if (i->flags.negcached) {
95dc7ff4 734 ++IpcacheStats.negative_hits;
3ff65596 735 // ignore i->error_message: the caller just checks IP cache presence
62e76326 736 return NULL;
ecc3091b 737 } else {
95dc7ff4 738 ++IpcacheStats.hits;
62e76326 739 i->lastref = squid_curtime;
3ff65596 740 // ignore i->error_message: the caller just checks IP cache presence
62e76326 741 return &i->addrs;
30a4f2a8 742 }
62e76326 743
3ff65596 744 /* no entry [any more] */
2ffff82e 745
fd9c47d1 746 if (const auto addrs = ipcacheCheckNumeric(name)) {
95dc7ff4 747 ++IpcacheStats.numeric_hits;
62e76326 748 return addrs;
22b245f8 749 }
62e76326 750
95dc7ff4 751 ++IpcacheStats.misses;
62e76326 752
30a4f2a8 753 if (flags & IP_LOOKUP_IF_MISS)
7c16f24c 754 ipcache_nbgethostbyname(name, NULL, NULL);
62e76326 755
30a4f2a8 756 return NULL;
090089c4 757}
758
63be0a78 759/// \ingroup IPCacheInternal
b8d8561b 760static void
761ipcacheStatPrint(ipcache_entry * i, StoreEntry * sentry)
af00901c 762{
cc192b50 763 char buf[MAX_IPSTRLEN];
764
26ac0430 765 if (!sentry) {
fa84c01d 766 debugs(14, DBG_CRITICAL, HERE << "CRITICAL: sentry is NULL!");
71aab4cc 767 return;
cc192b50 768 }
769
26ac0430 770 if (!i) {
fa84c01d 771 debugs(14, DBG_CRITICAL, HERE << "CRITICAL: ipcache_entry is NULL!");
cc192b50 772 storeAppendPrintf(sentry, "CRITICAL ERROR\n");
773 return;
774 }
775
181b1adc 776 storeAppendPrintf(sentry, " %-32.32s %c%c %6d %6d %2d(%2d)",
62e76326 777 hashKeyStr(&i->hash),
778 i->flags.fromhosts ? 'H' : ' ',
779 i->flags.negcached ? 'N' : ' ',
780 (int) (squid_curtime - i->lastref),
781 (int) ((i->flags.fromhosts ? -1 : i->expires - squid_curtime)),
fd9c47d1
AR
782 static_cast<int>(i->addrs.size()),
783 static_cast<int>(i->addrs.badCount()));
62e76326 784
cc192b50 785 /** \par
786 * Negative-cached entries have no IPs listed. */
26ac0430 787 if (i->flags.negcached) {
cc192b50 788 storeAppendPrintf(sentry, "\n");
789 return;
52926044 790 }
62e76326 791
cc192b50 792 /** \par
d85b8894 793 * Cached entries have IPs listed with a BNF of: ip-address '-' ('OK'|'BAD') */
fd9c47d1
AR
794 bool firstLine = true;
795 for (const auto &addr: i->addrs.raw()) {
cc192b50 796 /* Display tidy-up: IPv6 are so big make the list vertical */
fd9c47d1
AR
797 const char *indent = firstLine ? "" : " ";
798 storeAppendPrintf(sentry, "%s %45.45s-%3s\n",
799 indent,
800 addr.ip.toStr(buf, MAX_IPSTRLEN),
801 addr.bad() ? "BAD" : "OK ");
802 firstLine = false;
cc192b50 803 }
af00901c 804}
090089c4 805
63be0a78 806/**
807 \ingroup IPCacheInternal
808 *
809 * process objects list
810 */
b8d8561b 811void
812stat_ipcache_get(StoreEntry * sentry)
090089c4 813{
7b04dad5 814 dlink_node *m;
815 assert(ip_table != NULL);
15576b6a 816 storeAppendPrintf(sentry, "IP Cache Statistics:\n");
ac49890a
CT
817 storeAppendPrintf(sentry, "IPcache Entries Cached: %d\n",
818 ipcacheCount());
15576b6a 819 storeAppendPrintf(sentry, "IPcache Requests: %d\n",
62e76326 820 IpcacheStats.requests);
22b245f8 821 storeAppendPrintf(sentry, "IPcache Hits: %d\n",
62e76326 822 IpcacheStats.hits);
22b245f8 823 storeAppendPrintf(sentry, "IPcache Negative Hits: %d\n",
62e76326 824 IpcacheStats.negative_hits);
22b245f8 825 storeAppendPrintf(sentry, "IPcache Numeric Hits: %d\n",
826 IpcacheStats.numeric_hits);
827 storeAppendPrintf(sentry, "IPcache Misses: %d\n",
62e76326 828 IpcacheStats.misses);
f2d71697 829 storeAppendPrintf(sentry, "IPcache Retrieved A: %d\n",
bae9832d 830 IpcacheStats.rr_a);
f2d71697 831 storeAppendPrintf(sentry, "IPcache Retrieved AAAA: %d\n",
bae9832d 832 IpcacheStats.rr_aaaa);
833 storeAppendPrintf(sentry, "IPcache Retrieved CNAME: %d\n",
834 IpcacheStats.rr_cname);
835 storeAppendPrintf(sentry, "IPcache CNAME-Only Response: %d\n",
836 IpcacheStats.cname_only);
22b245f8 837 storeAppendPrintf(sentry, "IPcache Invalid Request: %d\n",
838 IpcacheStats.invalid);
15576b6a 839 storeAppendPrintf(sentry, "\n\n");
840 storeAppendPrintf(sentry, "IP Cache Contents:\n\n");
cc192b50 841 storeAppendPrintf(sentry, " %-31.31s %3s %6s %6s %4s\n",
62e76326 842 "Hostname",
843 "Flg",
844 "lstref",
845 "TTL",
cc192b50 846 "N(b)");
62e76326 847
cc192b50 848 for (m = lru_list.head; m; m = m->next) {
849 assert( m->next != m );
62e76326 850 ipcacheStatPrint((ipcache_entry *)m->data, sentry);
cc192b50 851 }
852}
853
63be0a78 854/// \ingroup IPCacheAPI
b8d8561b 855void
0ee4272b 856ipcacheInvalidate(const char *name)
f900607e 857{
858 ipcache_entry *i;
62e76326 859
f900607e 860 if ((i = ipcache_get(name)) == NULL)
62e76326 861 return;
862
6c11e193 863 i->expires = squid_curtime;
62e76326 864
ecc3091b 865 /*
63be0a78 866 * NOTE, don't call ipcacheRelease here because we might be here due
ecc3091b 867 * to a thread started from a callback.
868 */
f900607e 869}
af00901c 870
63be0a78 871/// \ingroup IPCacheAPI
a12a049a 872void
873ipcacheInvalidateNegative(const char *name)
874{
875 ipcache_entry *i;
876
877 if ((i = ipcache_get(name)) == NULL)
878 return;
879
880 if (i->flags.negcached)
881 i->expires = squid_curtime;
882
883 /*
63be0a78 884 * NOTE, don't call ipcacheRelease here because we might be here due
a12a049a 885 * to a thread started from a callback.
886 */
887}
888
63be0a78 889/// \ingroup IPCacheAPI
fd9c47d1 890static const Dns::CachedIps *
0ee4272b 891ipcacheCheckNumeric(const char *name)
af00901c 892{
b7ac5457 893 Ip::Address ip;
fd9c47d1
AR
894 if (!ip.fromHost(name))
895 return nullptr;
62e76326 896
fd9c47d1
AR
897 debugs(14, 4, "HIT_BYPASS for " << name << "=" << ip);
898 static Dns::CachedIps static_addrs;
899 static_addrs.reset(ip);
e5f6c5c2 900 return &static_addrs;
af00901c 901}
8905d949 902
63be0a78 903/// \ingroup IPCacheInternal
b8d8561b 904static void
905ipcacheLockEntry(ipcache_entry * i)
620da955 906{
7b04dad5 907 if (i->locks++ == 0) {
62e76326 908 dlinkDelete(&i->lru, &lru_list);
909 dlinkAdd(i, &i->lru, &lru_list);
7b04dad5 910 }
620da955 911}
912
63be0a78 913/// \ingroup IPCacheInternal
b8d8561b 914static void
915ipcacheUnlockEntry(ipcache_entry * i)
620da955 916{
26ac0430 917 if (i->locks < 1) {
e0236918 918 debugs(14, DBG_IMPORTANT, "WARNING: ipcacheEntry unlocked with no lock! locks=" << i->locks);
cc192b50 919 return;
920 }
921
5e263176 922 -- i->locks;
62e76326 923
620da955 924 if (ipcacheExpiredEntry(i))
62e76326 925 ipcacheRelease(i);
620da955 926}
e5f6c5c2 927
fd9c47d1
AR
928/// find the next good IP, wrapping if needed
929/// \returns whether the search was successful
930bool
931Dns::CachedIps::seekNewGood(const char *name)
52926044 932{
fd9c47d1
AR
933 // linear search!
934 for (size_t seen = 0; seen < ips.size(); ++seen) {
935 if (++goodPosition >= ips.size())
936 goodPosition = 0;
937 if (!ips[goodPosition].bad()) {
938 debugs(14, 3, "succeeded for " << name << ": " << *this);
939 return true;
940 }
52926044 941 }
fd9c47d1
AR
942 goodPosition = ips.size();
943 debugs(14, 3, "failed for " << name << ": " << *this);
944 return false;
945}
62e76326 946
fd9c47d1
AR
947void
948Dns::CachedIps::reset(const Ip::Address &ip)
949{
9770c5fe
AR
950 ips.clear();
951 ips.emplace_back(ip);
fd9c47d1
AR
952 goodPosition = 0;
953 // Assume that the given IP is good because CachedIps are designed to never
954 // run out of good IPs.
955 badCount_ = 0;
956}
62e76326 957
fd9c47d1
AR
958/// makes current() calls possible after a successful markAsBad()
959void
960Dns::CachedIps::restoreGoodness(const char *name)
961{
962 if (badCount() >= size()) {
963 // There are no good IPs left. Clear all bad marks. This must help
964 // because we are called only after a good address was tested as bad.
965 for (auto &cachedIp: ips)
966 cachedIp.forgetMarking();
967 badCount_ = 0;
52926044 968 }
fd9c47d1
AR
969 Must(seekNewGood(name));
970 debugs(14, 3, "cleared all IPs for " << name << "; now back to " << *this);
971}
62e76326 972
fd9c47d1
AR
973bool
974Dns::CachedIps::have(const Ip::Address &ip, size_t *positionOrNil) const
975{
976 // linear search!
977 size_t pos = 0;
978 for (const auto &cachedIp: ips) {
979 if (cachedIp.ip == ip) {
980 if (auto position = positionOrNil)
981 *position = pos;
9770c5fe 982 debugs(14, 7, ip << " at " << pos << " in " << *this);
fd9c47d1
AR
983 return true;
984 }
52926044 985 }
fd9c47d1 986 // no such address; leave *position as is
9770c5fe 987 debugs(14, 7, " no " << ip << " in " << *this);
fd9c47d1 988 return false;
52926044 989}
e5f6c5c2 990
991void
fd9c47d1 992Dns::CachedIps::pushUnique(const Ip::Address &ip)
e5f6c5c2 993{
fd9c47d1
AR
994 assert(!have(ip));
995 ips.emplace_back(ip);
996 assert(!raw().back().bad());
997}
62e76326 998
fd9c47d1
AR
999void
1000Dns::CachedIps::reportCurrent(std::ostream &os) const
1001{
1002 if (empty())
1003 os << "[no cached IPs]";
1004 else if (goodPosition == size())
1005 os << "[" << size() << " bad cached IPs]"; // could only be temporary
1006 else
1007 os << current() << " #" << (goodPosition+1) << "/" << ips.size() << "-" << badCount();
1008}
62e76326 1009
fd9c47d1
AR
1010void
1011Dns::CachedIps::markAsBad(const char *name, const Ip::Address &ip)
1012{
1013 size_t badPosition = 0;
1014 if (!have(ip, &badPosition))
1015 return; // no such address
62e76326 1016
fd9c47d1
AR
1017 auto &cachedIp = ips[badPosition];
1018 if (cachedIp.bad())
1019 return; // already marked correctly
62e76326 1020
fd9c47d1
AR
1021 cachedIp.markAsBad();
1022 ++badCount_;
1023 debugs(14, 2, ip << " of " << name);
62e76326 1024
fd9c47d1
AR
1025 if (goodPosition == badPosition)
1026 restoreGoodness(name);
1027 // else nothing to do: goodPositon still points to a good IP
e5f6c5c2 1028}
56e15c50 1029
ec505200 1030void
fd9c47d1 1031Dns::CachedIps::forgetMarking(const char *name, const Ip::Address &ip)
ec505200 1032{
fd9c47d1
AR
1033 if (!badCount_)
1034 return; // all IPs are already "good"
ec505200 1035
fd9c47d1
AR
1036 size_t badPosition = 0;
1037 if (!have(ip, &badPosition))
1038 return; // no such address
ec505200 1039
fd9c47d1
AR
1040 auto &cachedIp = ips[badPosition];
1041 if (!cachedIp.bad())
1042 return; // already marked correctly
ec505200 1043
fd9c47d1
AR
1044 cachedIp.forgetMarking();
1045 assert(!cachedIp.bad());
1046 --badCount_;
1047 debugs(14, 2, ip << " of " << name);
1048}
ec505200 1049
fd9c47d1
AR
1050/**
1051 * Marks the given address as BAD.
1052 * Does nothing if the domain name does not exist.
1053 *
1054 \param name domain name to have an IP marked bad
1055 \param addr specific addres to be marked bad
1056 */
1057void
1058ipcacheMarkBadAddr(const char *name, const Ip::Address &addr)
1059{
1060 if (auto cached = ipcache_get(name))
1061 cached->addrs.markAsBad(name, addr);
ec505200
HN
1062}
1063
63be0a78 1064/// \ingroup IPCacheAPI
22c653cd 1065void
b7ac5457 1066ipcacheMarkGoodAddr(const char *name, const Ip::Address &addr)
22c653cd 1067{
fd9c47d1
AR
1068 if (auto cached = ipcache_get(name))
1069 cached->addrs.forgetMarking(name, addr);
22c653cd 1070}
1071
63be0a78 1072/// \ingroup IPCacheInternal
ec878047 1073static void
1074ipcacheFreeEntry(void *data)
1075{
e6ccf245 1076 ipcache_entry *i = (ipcache_entry *)data;
3c670b50
AJ
1077 delete i;
1078}
1079
1080ipcache_entry::~ipcache_entry()
1081{
3c670b50
AJ
1082 xfree(error_message);
1083 xfree(hash.key);
ec878047 1084}
1085
63be0a78 1086/// \ingroup IPCacheAPI
56e15c50 1087void
1088ipcacheFreeMemory(void)
1089{
ec878047 1090 hashFreeItems(ip_table, ipcacheFreeEntry);
56e15c50 1091 hashFreeMemory(ip_table);
afe95a7e 1092 ip_table = NULL;
56e15c50 1093}
3fb036e8 1094
63be0a78 1095/**
1096 \ingroup IPCacheAPI
1097 *
1098 * Recalculate IP cache size upon reconfigure.
1099 * Is called to clear the IPCache's data structures,
1100 * cancel all pending requests.
1101 */
429fdbec 1102void
1103ipcache_restart(void)
1104{
429fdbec 1105 ipcache_high = (long) (((float) Config.ipcache.size *
62e76326 1106 (float) Config.ipcache.high) / (float) 100);
429fdbec 1107 ipcache_low = (long) (((float) Config.ipcache.size *
62e76326 1108 (float) Config.ipcache.low) / (float) 100);
0e70aa1e 1109 purge_entries_fromhosts();
1110}
1111
63be0a78 1112/**
1113 \ingroup IPCacheAPI
1114 *
1115 * Adds a "static" entry from /etc/hosts
1116 *
f53969cc
SM
1117 \param name Hostname to be linked with IP
1118 \param ipaddr IP Address to be cached.
63be0a78 1119 *
f53969cc
SM
1120 \retval 0 Success.
1121 \retval 1 IP address is invalid or other error.
0e70aa1e 1122 */
1123int
1124ipcacheAddEntryFromHosts(const char *name, const char *ipaddr)
1125{
1126 ipcache_entry *i;
62e76326 1127
b7ac5457 1128 Ip::Address ip;
62e76326 1129
cc192b50 1130 if (!(ip = ipaddr)) {
62e76326 1131 if (strchr(ipaddr, ':') && strspn(ipaddr, "0123456789abcdefABCDEF:") == strlen(ipaddr)) {
bf8fe701 1132 debugs(14, 3, "ipcacheAddEntryFromHosts: Skipping IPv6 address '" << ipaddr << "'");
62e76326 1133 } else {
e0236918 1134 debugs(14, DBG_IMPORTANT, "ipcacheAddEntryFromHosts: Bad IP address '" << ipaddr << "'");
62e76326 1135 }
1136
1137 return 1;
0e70aa1e 1138 }
62e76326 1139
fd9c47d1
AR
1140 if (!Ip::EnableIpv6 && ip.isIPv6()) {
1141 debugs(14, 2, "skips IPv6 address in /etc/hosts because IPv6 support was disabled: " << ip);
1142 return 1;
1143 }
1144
0e70aa1e 1145 if ((i = ipcache_get(name))) {
62e76326 1146 if (1 == i->flags.fromhosts) {
1147 ipcacheUnlockEntry(i);
1148 } else if (i->locks > 0) {
e0236918 1149 debugs(14, DBG_IMPORTANT, "ipcacheAddEntryFromHosts: can't add static entry for locked name '" << name << "'");
62e76326 1150 return 1;
1151 } else {
1152 ipcacheRelease(i);
1153 }
0e70aa1e 1154 }
62e76326 1155
3c670b50 1156 i = new ipcache_entry(name);
fd9c47d1 1157 i->addrs.pushUnique(ip);
be4d35dc 1158 i->flags.fromhosts = true;
0e70aa1e 1159 ipcacheAddEntry(i);
1160 ipcacheLockEntry(i);
1161 return 0;
429fdbec 1162}
ce75f381 1163
59ad6d31 1164#if SQUID_SNMP
63be0a78 1165/**
1166 \ingroup IPCacheAPI
1167 *
135171fe 1168 * The function to return the ip cache statistics to via SNMP
1169 */
86115da5 1170variable_list *
1f5b542b 1171snmp_netIpFn(variable_list * Var, snint * ErrP)
d60c11be 1172{
736eb6ad 1173 variable_list *Answer = NULL;
6a644e75
AJ
1174 MemBuf tmp;
1175 debugs(49, 5, "snmp_netIpFn: Processing request:" << snmpDebugOid(Var->name, Var->name_length, tmp));
86115da5 1176 *ErrP = SNMP_ERR_NOERROR;
62e76326 1177
135171fe 1178 switch (Var->name[LEN_SQ_NET + 1]) {
62e76326 1179
1f5b542b 1180 case IP_ENT:
62e76326 1181 Answer = snmp_var_new_integer(Var->name, Var->name_length,
ac49890a 1182 ipcacheCount(),
62e76326 1183 SMI_GAUGE32);
1184 break;
1185
1f5b542b 1186 case IP_REQ:
62e76326 1187 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1188 IpcacheStats.requests,
1189 SMI_COUNTER32);
1190 break;
1191
1f5b542b 1192 case IP_HITS:
62e76326 1193 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1194 IpcacheStats.hits,
1195 SMI_COUNTER32);
1196 break;
1197
1f5b542b 1198 case IP_PENDHIT:
62e76326 1199 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1200 0,
1201 SMI_GAUGE32);
1202 break;
1203
1f5b542b 1204 case IP_NEGHIT:
62e76326 1205 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1206 IpcacheStats.negative_hits,
1207 SMI_COUNTER32);
1208 break;
1209
1f5b542b 1210 case IP_MISS:
62e76326 1211 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1212 IpcacheStats.misses,
1213 SMI_COUNTER32);
1214 break;
1215
1f5b542b 1216 case IP_GHBN:
62e76326 1217 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1218 0, /* deprecated */
1219 SMI_COUNTER32);
1220 break;
1221
1f5b542b 1222 case IP_LOC:
62e76326 1223 Answer = snmp_var_new_integer(Var->name, Var->name_length,
1224 0, /* deprecated */
1225 SMI_COUNTER32);
1226 break;
1227
ce75f381 1228 default:
62e76326 1229 *ErrP = SNMP_ERR_NOSUCHNAME;
1230 snmp_var_free(Answer);
1231 return (NULL);
86115da5 1232 }
62e76326 1233
86115da5 1234 return Answer;
ce75f381 1235}
1f5b542b 1236
135171fe 1237#endif /*SQUID_SNMP */
f53969cc 1238