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