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