]>
Commit | Line | Data |
---|---|---|
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 |
71 | template <class Content> | |
72 | class RrSpecs | |
73 | { | |
74 | public: | |
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 | |
81 | class IpCacheLookupForwarder | |
82 | { | |
83 | public: | |
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 | ||
107 | protected: | |
108 | /// \returns not yet reported lookup delay in milliseconds | |
109 | int additionalLookupDelay() const { return tvSubMsec(lastLookupEnd, current_time); } | |
110 | ||
111 | private: | |
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 |
129 | class ipcache_entry |
130 | { | |
fd9c47d1 | 131 | CBDATA_CLASS(ipcache_entry); |
3c670b50 | 132 | |
3ff65596 | 133 | public: |
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 | ||
169 | protected: | |
170 | void updateTtl(const unsigned int rrTtl); | |
ecc3091b | 171 | }; |
172 | ||
63be0a78 | 173 | /// \ingroup IPCacheInternal |
26ac0430 | 174 | static 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 | 189 | static dlink_list lru_list; |
7b04dad5 | 190 | |
59a09b98 FC |
191 | // forward-decls |
192 | static void stat_ipcache_get(StoreEntry *); | |
193 | ||
74addf6c | 194 | static FREE ipcacheFreeEntry; |
7b724b86 | 195 | static IDNSCB ipcacheHandleReply; |
74addf6c | 196 | static int ipcacheExpiredEntry(ipcache_entry *); |
f5b8bbc4 | 197 | static ipcache_entry *ipcache_get(const char *); |
74addf6c | 198 | static void ipcacheLockEntry(ipcache_entry *); |
f5b8bbc4 | 199 | static void ipcacheStatPrint(ipcache_entry *, StoreEntry *); |
200 | static void ipcacheUnlockEntry(ipcache_entry *); | |
cc192b50 | 201 | static void ipcacheRelease(ipcache_entry *, bool dofree = true); |
fd9c47d1 AR |
202 | static const Dns::CachedIps *ipcacheCheckNumeric(const char *name); |
203 | static void ipcache_nbgethostbyname_(const char *name, IpCacheLookupForwarder handler); | |
30a4f2a8 | 204 | |
63be0a78 | 205 | /// \ingroup IPCacheInternal |
365e5b34 | 206 | static hash_table *ip_table = NULL; |
090089c4 | 207 | |
63be0a78 | 208 | /// \ingroup IPCacheInternal |
24382924 | 209 | static long ipcache_low = 180; |
63be0a78 | 210 | /// \ingroup IPCacheInternal |
24382924 | 211 | static long ipcache_high = 200; |
f88bb09c | 212 | |
c021888f | 213 | #if LIBRESOLV_DNS_TTL_HACK |
214 | extern int _dns_ttl_; | |
215 | #endif | |
216 | ||
fd9c47d1 | 217 | CBDATA_CLASS_INIT(ipcache_entry); |
ac49890a | 218 | |
fd9c47d1 AR |
219 | IpCacheLookupForwarder::IpCacheLookupForwarder(const CbcPointer<Dns::IpReceiver> &receiver): |
220 | receiverObj(receiver) | |
221 | { | |
222 | } | |
223 | ||
224 | IpCacheLookupForwarder::IpCacheLookupForwarder(IPH *fun, void *data): | |
225 | receiverFun(fun), receiverData(data) | |
3ff65596 | 226 | { |
3ff65596 AR |
227 | } |
228 | ||
fd9c47d1 AR |
229 | void |
230 | IpCacheLookupForwarder::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 | |
248 | bool | |
249 | IpCacheLookupForwarder::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 | |
264 | void | |
265 | IpCacheLookupForwarder::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 | ||
276 | void | |
277 | IpCacheLookupForwarder::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 | |
291 | inline int ipcacheCount() { return ip_table ? ip_table->count : 0; } | |
292 | ||
63be0a78 | 293 | /** |
294 | \ingroup IPCacheInternal | |
295 | * | |
296 | * removes the given ipcache entry | |
297 | */ | |
b8d8561b | 298 | static void |
cc192b50 | 299 | ipcacheRelease(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 | 320 | static ipcache_entry * |
0ee4272b | 321 | ipcache_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 | 330 | static int |
331 | ipcacheExpiredEntry(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 | 349 | void |
ced8def3 | 350 | ipcache_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 | 382 | static void |
383 | purge_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 | 406 | ipcache_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 | 418 | static void |
ecc3091b | 419 | ipcacheAddEntry(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 | 439 | static void |
fd9c47d1 | 440 | ipcacheCallback(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 |
454 | void |
455 | ipcache_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 | 462 | static void |
33ab4aaf | 463 | ipcacheParse(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 | ||
507 | template <class Specs> | |
508 | void | |
509 | ipcache_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 |
537 | void |
538 | ipcache_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 | 551 | static void |
fd9c47d1 | 552 | ipcacheHandleReply(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 | 598 | void |
8407afee | 599 | ipcache_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 | ||
605 | void | |
606 | Dns::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 | |
613 | static void | |
614 | ipcache_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 |
670 | static void | |
671 | ipcacheRegisterWithCacheManager(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 | 685 | void |
0673c0ba | 686 | ipcache_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 | 718 | const ipcache_addrs * |
719 | ipcache_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 | 759 | static void |
760 | ipcacheStatPrint(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 | 810 | void |
811 | stat_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 | 854 | void |
0ee4272b | 855 | ipcacheInvalidate(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 | 871 | void |
872 | ipcacheInvalidateNegative(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 | 889 | static const Dns::CachedIps * |
0ee4272b | 890 | ipcacheCheckNumeric(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 | 903 | static void |
904 | ipcacheLockEntry(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 | 913 | static void |
914 | ipcacheUnlockEntry(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 | |
929 | bool | |
930 | Dns::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 |
946 | void |
947 | Dns::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() |
958 | void | |
959 | Dns::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 |
972 | bool |
973 | Dns::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 | |
990 | void | |
fd9c47d1 | 991 | Dns::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 |
998 | void |
999 | Dns::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 |
1009 | void |
1010 | Dns::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 | 1029 | void |
fd9c47d1 | 1030 | Dns::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 | */ |
1056 | void | |
1057 | ipcacheMarkBadAddr(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 | 1064 | void |
b7ac5457 | 1065 | ipcacheMarkGoodAddr(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 | 1072 | static void |
1073 | ipcacheFreeEntry(void *data) | |
1074 | { | |
e6ccf245 | 1075 | ipcache_entry *i = (ipcache_entry *)data; |
3c670b50 AJ |
1076 | delete i; |
1077 | } | |
1078 | ||
1079 | ipcache_entry::~ipcache_entry() | |
1080 | { | |
3c670b50 AJ |
1081 | xfree(error_message); |
1082 | xfree(hash.key); | |
ec878047 | 1083 | } |
1084 | ||
63be0a78 | 1085 | /// \ingroup IPCacheAPI |
56e15c50 | 1086 | void |
1087 | ipcacheFreeMemory(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 | 1101 | void |
1102 | ipcache_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 | */ |
1122 | int | |
1123 | ipcacheAddEntryFromHosts(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 | 1169 | variable_list * |
1f5b542b | 1170 | snmp_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 |