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