]>
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" |
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 |
70 | template <class Content> | |
71 | class RrSpecs | |
72 | { | |
73 | public: | |
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 | |
80 | class IpCacheLookupForwarder | |
81 | { | |
82 | public: | |
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 | ||
106 | protected: | |
107 | /// \returns not yet reported lookup delay in milliseconds | |
108 | int additionalLookupDelay() const { return tvSubMsec(lastLookupEnd, current_time); } | |
109 | ||
110 | private: | |
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 |
128 | class ipcache_entry |
129 | { | |
fd9c47d1 | 130 | CBDATA_CLASS(ipcache_entry); |
3c670b50 | 131 | |
3ff65596 | 132 | public: |
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 | ||
168 | protected: | |
169 | void updateTtl(const unsigned int rrTtl); | |
ecc3091b | 170 | }; |
171 | ||
63be0a78 | 172 | /// \ingroup IPCacheInternal |
26ac0430 | 173 | static 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 | 188 | static dlink_list lru_list; |
7b04dad5 | 189 | |
59a09b98 FC |
190 | // forward-decls |
191 | static void stat_ipcache_get(StoreEntry *); | |
192 | ||
74addf6c | 193 | static FREE ipcacheFreeEntry; |
7b724b86 | 194 | static IDNSCB ipcacheHandleReply; |
74addf6c | 195 | static int ipcacheExpiredEntry(ipcache_entry *); |
f5b8bbc4 | 196 | static ipcache_entry *ipcache_get(const char *); |
74addf6c | 197 | static void ipcacheLockEntry(ipcache_entry *); |
f5b8bbc4 | 198 | static void ipcacheStatPrint(ipcache_entry *, StoreEntry *); |
199 | static void ipcacheUnlockEntry(ipcache_entry *); | |
cc192b50 | 200 | static void ipcacheRelease(ipcache_entry *, bool dofree = true); |
fd9c47d1 AR |
201 | static const Dns::CachedIps *ipcacheCheckNumeric(const char *name); |
202 | static void ipcache_nbgethostbyname_(const char *name, IpCacheLookupForwarder handler); | |
30a4f2a8 | 203 | |
63be0a78 | 204 | /// \ingroup IPCacheInternal |
365e5b34 | 205 | static hash_table *ip_table = NULL; |
090089c4 | 206 | |
63be0a78 | 207 | /// \ingroup IPCacheInternal |
24382924 | 208 | static long ipcache_low = 180; |
63be0a78 | 209 | /// \ingroup IPCacheInternal |
24382924 | 210 | static long ipcache_high = 200; |
f88bb09c | 211 | |
c021888f | 212 | #if LIBRESOLV_DNS_TTL_HACK |
213 | extern int _dns_ttl_; | |
214 | #endif | |
215 | ||
fd9c47d1 | 216 | CBDATA_CLASS_INIT(ipcache_entry); |
ac49890a | 217 | |
fd9c47d1 AR |
218 | IpCacheLookupForwarder::IpCacheLookupForwarder(const CbcPointer<Dns::IpReceiver> &receiver): |
219 | receiverObj(receiver) | |
220 | { | |
221 | } | |
222 | ||
223 | IpCacheLookupForwarder::IpCacheLookupForwarder(IPH *fun, void *data): | |
224 | receiverFun(fun), receiverData(data) | |
3ff65596 | 225 | { |
3ff65596 AR |
226 | } |
227 | ||
fd9c47d1 AR |
228 | void |
229 | IpCacheLookupForwarder::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 | |
247 | bool | |
248 | IpCacheLookupForwarder::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 | |
263 | void | |
264 | IpCacheLookupForwarder::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 | ||
275 | void | |
276 | IpCacheLookupForwarder::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 | |
290 | inline int ipcacheCount() { return ip_table ? ip_table->count : 0; } | |
291 | ||
63be0a78 | 292 | /** |
293 | \ingroup IPCacheInternal | |
294 | * | |
295 | * removes the given ipcache entry | |
296 | */ | |
b8d8561b | 297 | static void |
cc192b50 | 298 | ipcacheRelease(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 | 319 | static ipcache_entry * |
0ee4272b | 320 | ipcache_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 | 329 | static int |
330 | ipcacheExpiredEntry(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 | 348 | void |
ced8def3 | 349 | ipcache_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 | 381 | static void |
382 | purge_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 | 405 | ipcache_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 | 417 | static void |
ecc3091b | 418 | ipcacheAddEntry(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 | 438 | static void |
fd9c47d1 | 439 | ipcacheCallback(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 |
453 | void |
454 | ipcache_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 | 461 | static void |
33ab4aaf | 462 | ipcacheParse(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 | ||
506 | template <class Specs> | |
507 | void | |
508 | ipcache_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 |
536 | void |
537 | ipcache_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 | 550 | static void |
fd9c47d1 | 551 | ipcacheHandleReply(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 | 597 | void |
8407afee | 598 | ipcache_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 | ||
604 | void | |
605 | Dns::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 | |
612 | static void | |
613 | ipcache_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 |
669 | static void | |
670 | ipcacheRegisterWithCacheManager(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 | 684 | void |
0673c0ba | 685 | ipcache_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 | 717 | const ipcache_addrs * |
718 | ipcache_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 | 758 | static void |
759 | ipcacheStatPrint(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 | 809 | void |
810 | stat_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 | 853 | void |
0ee4272b | 854 | ipcacheInvalidate(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 | 870 | void |
871 | ipcacheInvalidateNegative(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 | 888 | static const Dns::CachedIps * |
0ee4272b | 889 | ipcacheCheckNumeric(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 | 902 | static void |
903 | ipcacheLockEntry(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 | 912 | static void |
913 | ipcacheUnlockEntry(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 | |
928 | bool | |
929 | Dns::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 |
945 | void |
946 | Dns::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() |
957 | void | |
958 | Dns::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 |
971 | bool |
972 | Dns::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 | |
989 | void | |
fd9c47d1 | 990 | Dns::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 |
997 | void |
998 | Dns::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 |
1008 | void |
1009 | Dns::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 | 1028 | void |
fd9c47d1 | 1029 | Dns::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 | */ |
1055 | void | |
1056 | ipcacheMarkBadAddr(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 | 1063 | void |
b7ac5457 | 1064 | ipcacheMarkGoodAddr(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 | 1071 | static void |
1072 | ipcacheFreeEntry(void *data) | |
1073 | { | |
e6ccf245 | 1074 | ipcache_entry *i = (ipcache_entry *)data; |
3c670b50 AJ |
1075 | delete i; |
1076 | } | |
1077 | ||
1078 | ipcache_entry::~ipcache_entry() | |
1079 | { | |
3c670b50 AJ |
1080 | xfree(error_message); |
1081 | xfree(hash.key); | |
ec878047 | 1082 | } |
1083 | ||
63be0a78 | 1084 | /// \ingroup IPCacheAPI |
56e15c50 | 1085 | void |
1086 | ipcacheFreeMemory(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 | 1100 | void |
1101 | ipcache_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 | */ |
1121 | int | |
1122 | ipcacheAddEntryFromHosts(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 | 1168 | variable_list * |
1f5b542b | 1169 | snmp_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 |