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