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