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