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