]>
Commit | Line | Data |
---|---|---|
7921dbc5 | 1 | |
30a4f2a8 | 2 | /* |
c68e9c6b | 3 | * $Id: ipcache.cc,v 1.206 1998/11/12 06:28:13 wessels Exp $ |
30a4f2a8 | 4 | * |
5 | * DEBUG: section 14 IP Cache | |
6 | * AUTHOR: Harvest Derived | |
7 | * | |
42c04c16 | 8 | * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ |
e25c139f | 9 | * ---------------------------------------------------------- |
30a4f2a8 | 10 | * |
11 | * Squid is the result of efforts by numerous individuals from the | |
12 | * Internet community. Development is led by Duane Wessels of the | |
e25c139f | 13 | * National Laboratory for Applied Network Research and funded by the |
14 | * National Science Foundation. Squid is Copyrighted (C) 1998 by | |
15 | * Duane Wessels and the University of California San Diego. Please | |
16 | * see the COPYRIGHT file for full details. Squid incorporates | |
17 | * software developed and/or copyrighted by other sources. Please see | |
18 | * the CREDITS file for full details. | |
30a4f2a8 | 19 | * |
20 | * This program is free software; you can redistribute it and/or modify | |
21 | * it under the terms of the GNU General Public License as published by | |
22 | * the Free Software Foundation; either version 2 of the License, or | |
23 | * (at your option) any later version. | |
24 | * | |
25 | * This program is distributed in the hope that it will be useful, | |
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
28 | * GNU General Public License for more details. | |
29 | * | |
30 | * You should have received a copy of the GNU General Public License | |
31 | * along with this program; if not, write to the Free Software | |
cbdec147 | 32 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. |
e25c139f | 33 | * |
019dd986 | 34 | */ |
44a47c6e | 35 | |
36 | #include "squid.h" | |
37 | ||
30a4f2a8 | 38 | static struct { |
39 | int requests; | |
f88bb09c | 40 | int replies; |
30a4f2a8 | 41 | int hits; |
42 | int misses; | |
43 | int pending_hits; | |
44 | int negative_hits; | |
30a4f2a8 | 45 | int errors; |
30a4f2a8 | 46 | int ghbn_calls; /* # calls to blocking gethostbyname() */ |
429fdbec | 47 | int release_locked; |
30a4f2a8 | 48 | } IpcacheStats; |
090089c4 | 49 | |
ce75f381 | 50 | static dlink_list lru_list; |
7b04dad5 | 51 | |
74addf6c | 52 | static FREE ipcacheFreeEntry; |
53 | static HLPCB ipcacheHandleReply; | |
54 | static IPH dummy_handler; | |
55 | static int ipcacheExpiredEntry(ipcache_entry *); | |
f5b8bbc4 | 56 | static int ipcache_testname(void); |
f5b8bbc4 | 57 | static ipcache_entry *ipcacheAddNew(const char *, const struct hostent *, ipcache_status_t); |
74addf6c | 58 | static ipcache_entry *ipcacheParse(const char *buf); |
59 | static ipcache_entry *ipcache_create(const char *name); | |
f5b8bbc4 | 60 | static ipcache_entry *ipcache_get(const char *); |
74addf6c | 61 | static void ipcacheAddHostent(ipcache_entry *, const struct hostent *); |
f5b8bbc4 | 62 | static void ipcacheAddPending(ipcache_entry *, IPH *, void *); |
74addf6c | 63 | static void ipcacheChangeKey(ipcache_entry * i); |
64 | static void ipcacheLockEntry(ipcache_entry *); | |
f5b8bbc4 | 65 | static void ipcacheStatPrint(ipcache_entry *, StoreEntry *); |
66 | static void ipcacheUnlockEntry(ipcache_entry *); | |
74addf6c | 67 | static void ipcache_call_pending(ipcache_entry *); |
68 | static void ipcache_release(ipcache_entry *); | |
30a4f2a8 | 69 | |
e5f6c5c2 | 70 | static ipcache_addrs static_addrs; |
365e5b34 | 71 | static hash_table *ip_table = NULL; |
090089c4 | 72 | |
30a4f2a8 | 73 | static char ipcache_status_char[] = |
090089c4 | 74 | { |
30a4f2a8 | 75 | 'C', |
76 | 'N', | |
77 | 'P', | |
78 | 'D' | |
79 | }; | |
090089c4 | 80 | |
24382924 | 81 | static long ipcache_low = 180; |
82 | static long ipcache_high = 200; | |
f88bb09c | 83 | |
c021888f | 84 | #if LIBRESOLV_DNS_TTL_HACK |
85 | extern int _dns_ttl_; | |
86 | #endif | |
87 | ||
b8d8561b | 88 | static int |
0673c0ba | 89 | ipcache_testname(void) |
090089c4 | 90 | { |
b09ad9fd | 91 | wordlist *w = NULL; |
a3d5953d | 92 | debug(14, 1) ("Performing DNS Tests...\n"); |
b6f794d6 | 93 | if ((w = Config.dns_testname_list) == NULL) |
b09ad9fd | 94 | return 1; |
95 | for (; w; w = w->next) { | |
30a4f2a8 | 96 | IpcacheStats.ghbn_calls++; |
b09ad9fd | 97 | if (gethostbyname(w->key) != NULL) |
98 | return 1; | |
090089c4 | 99 | } |
b09ad9fd | 100 | return 0; |
090089c4 | 101 | } |
102 | ||
090089c4 | 103 | /* removes the given ipcache entry */ |
b8d8561b | 104 | static void |
105 | ipcache_release(ipcache_entry * i) | |
090089c4 | 106 | { |
30a4f2a8 | 107 | hash_link *table_entry = NULL; |
2b19063a | 108 | if ((table_entry = hash_lookup(ip_table, i->name)) == NULL) { |
109 | snprintf(tmp_error_buf, ERROR_BUF_SZ, "ipcache_release: key '%s' not found\n", i->name); | |
110 | fatal_dump(tmp_error_buf); | |
111 | } | |
365e5b34 | 112 | assert(i == (ipcache_entry *) table_entry); |
429fdbec | 113 | if (i->locks) { |
114 | i->expires = squid_curtime; | |
edeb28fd | 115 | ipcacheChangeKey(i); |
429fdbec | 116 | IpcacheStats.release_locked++; |
30a4f2a8 | 117 | return; |
090089c4 | 118 | } |
3fda0827 | 119 | hash_remove_link(ip_table, table_entry); |
7b04dad5 | 120 | dlinkDelete(&i->lru, &lru_list); |
f7a5493b | 121 | if (i->status == IP_CACHED) { |
e5f6c5c2 | 122 | safe_free(i->addrs.in_addrs); |
22c653cd | 123 | safe_free(i->addrs.bad_mask); |
a3d5953d | 124 | debug(14, 5) ("ipcache_release: Released IP cached record for '%s'.\n", |
f7a5493b | 125 | i->name); |
30a4f2a8 | 126 | } |
f7a5493b | 127 | safe_free(i->name); |
128 | safe_free(i->error_message); | |
59c4d35b | 129 | memFree(MEM_IPCACHE_ENTRY, i); |
30a4f2a8 | 130 | return; |
090089c4 | 131 | } |
132 | ||
b8d8561b | 133 | static ipcache_entry * |
0ee4272b | 134 | ipcache_get(const char *name) |
090089c4 | 135 | { |
7b04dad5 | 136 | assert(ip_table != NULL); |
137 | return (ipcache_entry *) hash_lookup(ip_table, name); | |
090089c4 | 138 | } |
139 | ||
b8d8561b | 140 | static int |
141 | ipcacheExpiredEntry(ipcache_entry * i) | |
30a4f2a8 | 142 | { |
30a4f2a8 | 143 | if (i->status == IP_PENDING) |
144 | return 0; | |
145 | if (i->status == IP_DISPATCHED) | |
146 | return 0; | |
620da955 | 147 | if (i->locks != 0) |
148 | return 0; | |
7c63efed | 149 | if (i->addrs.count == 0) |
150 | return 1; | |
af00901c | 151 | if (i->expires > squid_curtime) |
30a4f2a8 | 152 | return 0; |
153 | return 1; | |
154 | } | |
090089c4 | 155 | |
7b04dad5 | 156 | void |
157 | ipcache_purgelru(void *voidnotused) | |
158 | { | |
159 | dlink_node *m; | |
160 | dlink_node *prev = NULL; | |
161 | ipcache_entry *i; | |
162 | int removed = 0; | |
52040193 | 163 | eventAdd("ipcache_purgelru", ipcache_purgelru, NULL, 10.0, 1); |
7b04dad5 | 164 | for (m = lru_list.tail; m; m = prev) { |
59c4d35b | 165 | if (memInUse(MEM_IPCACHE_ENTRY) < ipcache_low) |
7b04dad5 | 166 | break; |
167 | prev = m->prev; | |
168 | i = m->data; | |
169 | if (i->status == IP_PENDING) | |
170 | continue; | |
171 | if (i->status == IP_DISPATCHED) | |
172 | continue; | |
173 | if (i->locks != 0) | |
174 | continue; | |
175 | ipcache_release(i); | |
176 | removed++; | |
177 | } | |
178 | debug(14, 3) ("ipcache_purgelru: removed %d entries\n", removed); | |
179 | } | |
180 | ||
090089c4 | 181 | /* create blank ipcache_entry */ |
b8d8561b | 182 | static ipcache_entry * |
0ee4272b | 183 | ipcache_create(const char *name) |
090089c4 | 184 | { |
7b04dad5 | 185 | static ipcache_entry *i; |
59c4d35b | 186 | i = memAllocate(MEM_IPCACHE_ENTRY); |
7b04dad5 | 187 | i->name = xstrdup(name); |
188 | i->expires = squid_curtime + Config.negativeDnsTtl; | |
189 | hash_join(ip_table, (hash_link *) i); | |
190 | dlinkAdd(i, &i->lru, &lru_list); | |
191 | return i; | |
090089c4 | 192 | } |
193 | ||
b8d8561b | 194 | static void |
0ee4272b | 195 | ipcacheAddHostent(ipcache_entry * i, const struct hostent *hp) |
090089c4 | 196 | { |
f6c78bd2 | 197 | int addr_count = 0; |
198 | int k; | |
199 | safe_free(i->addrs.in_addrs); | |
22c653cd | 200 | safe_free(i->addrs.bad_mask); |
f6c78bd2 | 201 | while ((addr_count < 255) && *(hp->h_addr_list + addr_count)) |
202 | ++addr_count; | |
203 | i->addrs.count = (unsigned char) addr_count; | |
204 | i->addrs.in_addrs = xcalloc(addr_count, sizeof(struct in_addr)); | |
22c653cd | 205 | i->addrs.bad_mask = xcalloc(addr_count, sizeof(unsigned char)); |
206 | i->addrs.badcount = 0; | |
f6c78bd2 | 207 | for (k = 0; k < addr_count; k++) |
3c0117c9 | 208 | xmemcpy(&i->addrs.in_addrs[k].s_addr, |
f6c78bd2 | 209 | *(hp->h_addr_list + k), |
210 | hp->h_length); | |
dd7ad0a4 | 211 | } |
090089c4 | 212 | |
dd7ad0a4 | 213 | static ipcache_entry * |
0ee4272b | 214 | ipcacheAddNew(const char *name, const struct hostent *hp, ipcache_status_t status) |
dd7ad0a4 | 215 | { |
216 | ipcache_entry *i; | |
30a4f2a8 | 217 | if (ipcache_get(name)) |
c4170f1b | 218 | fatal_dump("ipcacheAddNew: somebody adding a duplicate!"); |
a3d5953d | 219 | debug(14, 10) ("ipcacheAddNew: Adding '%s', status=%c\n", |
dd7ad0a4 | 220 | name, |
221 | ipcache_status_char[status]); | |
222 | i = ipcache_create(name); | |
223 | if (hp) | |
224 | ipcacheAddHostent(i, hp); | |
225 | i->status = status; | |
226 | i->lastref = squid_curtime; | |
227 | return i; | |
090089c4 | 228 | } |
229 | ||
090089c4 | 230 | /* walks down the pending list, calling handlers */ |
b8d8561b | 231 | static void |
232 | ipcache_call_pending(ipcache_entry * i) | |
090089c4 | 233 | { |
59c4d35b | 234 | ip_pending *p = NULL; |
090089c4 | 235 | int nhandler = 0; |
30a4f2a8 | 236 | i->lastref = squid_curtime; |
620da955 | 237 | ipcacheLockEntry(i); |
30a4f2a8 | 238 | while (i->pending_head != NULL) { |
365e5b34 | 239 | p = i->pending_head; |
240 | i->pending_head = p->next; | |
241 | if (p->handler) { | |
242 | nhandler++; | |
243 | dns_error_message = i->error_message; | |
244 | if (cbdataValid(p->handlerData)) { | |
245 | p->handler(i->status == IP_CACHED ? &i->addrs : NULL, | |
246 | p->handlerData); | |
8407afee | 247 | } |
365e5b34 | 248 | cbdataUnlock(p->handlerData); |
249 | } | |
59c4d35b | 250 | memFree(MEM_IPCACHE_PENDING, p); |
090089c4 | 251 | } |
365e5b34 | 252 | i->pending_head = NULL; /* nuke list */ |
a3d5953d | 253 | debug(14, 10) ("ipcache_call_pending: Called %d handlers.\n", nhandler); |
620da955 | 254 | ipcacheUnlockEntry(i); |
090089c4 | 255 | } |
256 | ||
b8d8561b | 257 | static ipcache_entry * |
74addf6c | 258 | ipcacheParse(const char *inbuf) |
090089c4 | 259 | { |
bd34f258 | 260 | LOCAL_ARRAY(char, buf, DNS_INBUF_SZ); |
d5a266cb | 261 | char *token; |
262 | static ipcache_entry i; | |
bd34f258 | 263 | int j; |
d5a266cb | 264 | int k; |
bd34f258 | 265 | int ipcount = 0; |
266 | int ttl; | |
267 | char A[32][16]; | |
bd34f258 | 268 | memset(&i, '\0', sizeof(i)); |
269 | i.expires = squid_curtime; | |
270 | i.status = IP_NEGATIVE_CACHED; | |
c68e9c6b | 271 | if (inbuf == NULL) { |
272 | debug(14, 1) ("ipcacheParse: Got <NULL> reply\n"); | |
273 | return &i; | |
274 | } | |
275 | xstrncpy(buf, inbuf, DNS_INBUF_SZ); | |
276 | debug(14, 5) ("ipcacheParse: parsing:%s\n", buf); | |
bd34f258 | 277 | token = strtok(buf, w_space); |
278 | if (NULL == token) { | |
279 | debug(14, 1) ("ipcacheParse: Got <NULL>, expecting '$addr'\n"); | |
280 | return &i; | |
281 | } | |
282 | if (0 == strcmp(token, "$fail")) { | |
283 | i.expires = squid_curtime + Config.negativeDnsTtl; | |
284 | token = strtok(NULL, "\n"); | |
285 | assert(NULL != token); | |
286 | i.error_message = xstrdup(token); | |
287 | return &i; | |
288 | } | |
289 | if (0 != strcmp(token, "$addr")) { | |
290 | debug(14, 1) ("ipcacheParse: Got '%s', expecting '$addr'\n", token); | |
291 | return &i; | |
292 | } | |
293 | token = strtok(NULL, w_space); | |
294 | if (NULL == token) { | |
295 | debug(14, 1) ("ipcacheParse: Got <NULL>, expecting TTL\n"); | |
296 | return &i; | |
297 | } | |
298 | i.status = IP_CACHED; | |
299 | ttl = atoi(token); | |
300 | if (ttl > 0) | |
301 | i.expires = squid_curtime + ttl; | |
302 | else | |
303 | i.expires = squid_curtime + Config.positiveDnsTtl; | |
304 | while (NULL != (token = strtok(NULL, w_space))) { | |
305 | xstrncpy(A[ipcount], token, 16); | |
306 | if (++ipcount == 32) | |
090089c4 | 307 | break; |
090089c4 | 308 | } |
bd34f258 | 309 | if (0 == ipcount) { |
310 | i.addrs.in_addrs = NULL; | |
311 | i.addrs.bad_mask = NULL; | |
312 | } else { | |
313 | i.addrs.in_addrs = xcalloc(ipcount, sizeof(struct in_addr)); | |
314 | i.addrs.bad_mask = xcalloc(ipcount, sizeof(unsigned char)); | |
315 | } | |
316 | for (j = 0, k = 0; k < ipcount; k++) { | |
317 | if (safe_inet_addr(A[k], &i.addrs.in_addrs[j])) | |
318 | j++; | |
319 | else | |
320 | debug(14, 1) ("ipcacheParse: Invalid IP address '%s'\n", A[k]); | |
321 | } | |
322 | i.addrs.count = (unsigned char) j; | |
d5a266cb | 323 | return &i; |
090089c4 | 324 | } |
325 | ||
a7e59001 | 326 | static void |
74addf6c | 327 | ipcacheHandleReply(void *data, char *reply) |
090089c4 | 328 | { |
30a4f2a8 | 329 | int n; |
74addf6c | 330 | generic_cbdata *c = data; |
331 | ipcache_entry *i = c->data; | |
d5a266cb | 332 | ipcache_entry *x = NULL; |
e144eae4 | 333 | assert(i->status == IP_DISPATCHED); |
74addf6c | 334 | assert(i->locks); |
335 | cbdataFree(c); | |
336 | c = NULL; | |
337 | n = ++IpcacheStats.replies; | |
338 | statHistCount(&Counter.dns.svc_time, tvSubMsec(i->request_time, current_time)); | |
c68e9c6b | 339 | x = ipcacheParse(reply); |
340 | assert(x); | |
341 | i->status = x->status; | |
342 | i->addrs = x->addrs; | |
343 | i->error_message = x->error_message; | |
344 | i->expires = x->expires; | |
345 | ipcache_call_pending(i); | |
74addf6c | 346 | ipcacheUnlockEntry(i); /* unlock from IP_DISPATCHED */ |
090089c4 | 347 | } |
348 | ||
b8d8561b | 349 | static void |
8407afee | 350 | ipcacheAddPending(ipcache_entry * i, IPH * handler, void *handlerData) |
30a4f2a8 | 351 | { |
59c4d35b | 352 | ip_pending *pending = memAllocate(MEM_IPCACHE_PENDING); |
353 | ip_pending **I = NULL; | |
af00901c | 354 | i->lastref = squid_curtime; |
30a4f2a8 | 355 | pending->handler = handler; |
8407afee | 356 | pending->handlerData = handlerData; |
357 | cbdataLock(handlerData); | |
30a4f2a8 | 358 | for (I = &(i->pending_head); *I; I = &((*I)->next)); |
359 | *I = pending; | |
360 | } | |
361 | ||
b8d8561b | 362 | void |
8407afee | 363 | ipcache_nbgethostbyname(const char *name, IPH * handler, void *handlerData) |
090089c4 | 364 | { |
30a4f2a8 | 365 | ipcache_entry *i = NULL; |
429fdbec | 366 | const ipcache_addrs *addrs = NULL; |
74addf6c | 367 | generic_cbdata *c; |
605bd345 | 368 | assert(handler != NULL); |
03a1ee42 | 369 | debug(14, 4) ("ipcache_nbgethostbyname: Name '%s'.\n", name); |
30a4f2a8 | 370 | IpcacheStats.requests++; |
090089c4 | 371 | if (name == NULL || name[0] == '\0') { |
a3d5953d | 372 | debug(14, 4) ("ipcache_nbgethostbyname: Invalid name!\n"); |
03a1ee42 | 373 | handler(NULL, handlerData); |
af00901c | 374 | return; |
375 | } | |
e5f6c5c2 | 376 | if ((addrs = ipcacheCheckNumeric(name))) { |
03a1ee42 | 377 | handler(addrs, handlerData); |
af00901c | 378 | return; |
090089c4 | 379 | } |
30a4f2a8 | 380 | if ((i = ipcache_get(name))) { |
381 | if (ipcacheExpiredEntry(i)) { | |
382 | ipcache_release(i); | |
383 | i = NULL; | |
090089c4 | 384 | } |
090089c4 | 385 | } |
30a4f2a8 | 386 | if (i == NULL) { |
387 | /* MISS: No entry, create the new one */ | |
a3d5953d | 388 | debug(14, 5) ("ipcache_nbgethostbyname: MISS for '%s'\n", name); |
30a4f2a8 | 389 | IpcacheStats.misses++; |
dd7ad0a4 | 390 | i = ipcacheAddNew(name, NULL, IP_PENDING); |
8407afee | 391 | ipcacheAddPending(i, handler, handlerData); |
b87b92fb | 392 | i->request_time = current_time; |
30a4f2a8 | 393 | } else if (i->status == IP_CACHED || i->status == IP_NEGATIVE_CACHED) { |
394 | /* HIT */ | |
a3d5953d | 395 | debug(14, 4) ("ipcache_nbgethostbyname: HIT for '%s'\n", name); |
30a4f2a8 | 396 | if (i->status == IP_NEGATIVE_CACHED) |
397 | IpcacheStats.negative_hits++; | |
398 | else | |
399 | IpcacheStats.hits++; | |
8407afee | 400 | ipcacheAddPending(i, handler, handlerData); |
30a4f2a8 | 401 | ipcache_call_pending(i); |
af00901c | 402 | return; |
30a4f2a8 | 403 | } else if (i->status == IP_PENDING || i->status == IP_DISPATCHED) { |
a3d5953d | 404 | debug(14, 4) ("ipcache_nbgethostbyname: PENDING for '%s'\n", name); |
30a4f2a8 | 405 | IpcacheStats.pending_hits++; |
8407afee | 406 | ipcacheAddPending(i, handler, handlerData); |
7921dbc5 | 407 | if (squid_curtime - i->expires > 600) { |
5f6ac48b | 408 | debug(14, 0) ("ipcache_nbgethostbyname: '%s' PENDING for %d seconds, aborting\n", name, (int) (squid_curtime + Config.negativeDnsTtl - i->expires)); |
94af2e39 | 409 | ipcacheChangeKey(i); |
7921dbc5 | 410 | ipcache_call_pending(i); |
12001d13 | 411 | } |
af00901c | 412 | return; |
30a4f2a8 | 413 | } else { |
414 | fatal_dump("ipcache_nbgethostbyname: BAD ipcache_entry status"); | |
090089c4 | 415 | } |
74addf6c | 416 | /* for HIT, PENDING, DISPATCHED we've returned. For MISS we submit */ |
417 | c = xcalloc(1, sizeof(*c)); | |
418 | c->data = i; | |
419 | cbdataAdd(c, MEM_NONE); | |
620da955 | 420 | i->status = IP_DISPATCHED; |
74addf6c | 421 | ipcacheLockEntry(i); |
422 | dnsSubmit(i->name, ipcacheHandleReply, c); | |
090089c4 | 423 | } |
424 | ||
0ffd22bc | 425 | /* initialize the ipcache */ |
b8d8561b | 426 | void |
0673c0ba | 427 | ipcache_init(void) |
0ffd22bc | 428 | { |
aa9e2cab | 429 | int n; |
a3d5953d | 430 | debug(14, 3) ("Initializing IP Cache...\n"); |
30a4f2a8 | 431 | memset(&IpcacheStats, '\0', sizeof(IpcacheStats)); |
3eb55834 | 432 | memset(&lru_list, '\0', sizeof(lru_list)); |
0ffd22bc | 433 | /* test naming lookup */ |
30a4f2a8 | 434 | if (!opt_dns_tests) { |
a3d5953d | 435 | debug(14, 4) ("ipcache_init: Skipping DNS name lookup tests.\n"); |
b09ad9fd | 436 | } else if (!ipcache_testname()) { |
1d73e33a | 437 | fatal("ipcache_init: DNS name lookup tests failed."); |
0ffd22bc | 438 | } else { |
a3d5953d | 439 | debug(14, 1) ("Successful DNS name lookup tests...\n"); |
0ffd22bc | 440 | } |
e5f6c5c2 | 441 | memset(&static_addrs, '\0', sizeof(ipcache_addrs)); |
442 | static_addrs.in_addrs = xcalloc(1, sizeof(struct in_addr)); | |
22c653cd | 443 | static_addrs.bad_mask = xcalloc(1, sizeof(unsigned char)); |
b15e6857 | 444 | ipcache_high = (long) (((float) Config.ipcache.size * |
445 | (float) Config.ipcache.high) / (float) 100); | |
446 | ipcache_low = (long) (((float) Config.ipcache.size * | |
447 | (float) Config.ipcache.low) / (float) 100); | |
aa9e2cab | 448 | n = hashPrime(ipcache_high / 4); |
6a78c18e | 449 | ip_table = hash_create((HASHCMP *) strcmp, n, hash4); |
22f3fd98 | 450 | cachemgrRegister("ipcache", |
451 | "IP Cache Stats and Contents", | |
1da3b90b | 452 | stat_ipcache_get, 0, 1); |
090089c4 | 453 | } |
454 | ||
8407afee | 455 | int |
456 | ipcacheUnregister(const char *name, void *data) | |
090089c4 | 457 | { |
8407afee | 458 | ipcache_entry *i = NULL; |
59c4d35b | 459 | ip_pending *p = NULL; |
8407afee | 460 | int n = 0; |
2302bc23 | 461 | debug(14, 3) ("ipcacheUnregister: name '%s'\n", name); |
8407afee | 462 | if ((i = ipcache_get(name)) == NULL) |
365e5b34 | 463 | return 0; |
8407afee | 464 | if (i->status == IP_PENDING || i->status == IP_DISPATCHED) { |
365e5b34 | 465 | for (p = i->pending_head; p; p = p->next) { |
466 | if (p->handlerData != data) | |
467 | continue; | |
468 | p->handler = NULL; | |
469 | n++; | |
470 | } | |
8407afee | 471 | } |
f889842a | 472 | assert(n > 0); |
8407afee | 473 | debug(14, 3) ("ipcacheUnregister: unregistered %d handlers\n", n); |
474 | return n; | |
090089c4 | 475 | } |
476 | ||
0ee4272b | 477 | const ipcache_addrs * |
478 | ipcache_gethostbyname(const char *name, int flags) | |
090089c4 | 479 | { |
30a4f2a8 | 480 | ipcache_entry *i = NULL; |
e5f6c5c2 | 481 | ipcache_addrs *addrs; |
30a4f2a8 | 482 | if (!name) |
483 | fatal_dump("ipcache_gethostbyname: NULL name"); | |
a3d5953d | 484 | debug(14, 3) ("ipcache_gethostbyname: '%s', flags=%x\n", name, flags); |
30a4f2a8 | 485 | IpcacheStats.requests++; |
486 | if ((i = ipcache_get(name))) { | |
af00901c | 487 | if (ipcacheExpiredEntry(i)) { |
488 | ipcache_release(i); | |
489 | i = NULL; | |
490 | } | |
491 | } | |
492 | if (i) { | |
7c63efed | 493 | if (i->status == IP_NEGATIVE_CACHED) { |
30a4f2a8 | 494 | IpcacheStats.negative_hits++; |
495 | dns_error_message = i->error_message; | |
496 | return NULL; | |
8a8d4042 | 497 | } else if (i->addrs.count == 0) { |
498 | (void) 0; | |
090089c4 | 499 | } else { |
30a4f2a8 | 500 | IpcacheStats.hits++; |
501 | i->lastref = squid_curtime; | |
e5f6c5c2 | 502 | return &i->addrs; |
090089c4 | 503 | } |
30a4f2a8 | 504 | } |
e5f6c5c2 | 505 | if ((addrs = ipcacheCheckNumeric(name))) |
506 | return addrs; | |
e61d864a | 507 | IpcacheStats.misses++; |
30a4f2a8 | 508 | if (flags & IP_LOOKUP_IF_MISS) |
8407afee | 509 | ipcache_nbgethostbyname(name, dummy_handler, NULL); |
30a4f2a8 | 510 | return NULL; |
090089c4 | 511 | } |
512 | ||
b8d8561b | 513 | static void |
514 | ipcacheStatPrint(ipcache_entry * i, StoreEntry * sentry) | |
af00901c | 515 | { |
516 | int k; | |
15576b6a | 517 | storeAppendPrintf(sentry, " %-32.32s %c%c %6d %6d %2d(%2d)", |
af00901c | 518 | i->name, |
519 | ipcache_status_char[i->status], | |
233794cd | 520 | i->locks ? 'L' : ' ', |
af00901c | 521 | (int) (squid_curtime - i->lastref), |
522 | (int) (i->expires - squid_curtime), | |
22c653cd | 523 | (int) i->addrs.count, |
524 | (int) i->addrs.badcount); | |
52926044 | 525 | for (k = 0; k < (int) i->addrs.count; k++) { |
22c653cd | 526 | storeAppendPrintf(sentry, " %15s-%3s", inet_ntoa(i->addrs.in_addrs[k]), |
527 | i->addrs.bad_mask[k] ? "BAD" : "OK "); | |
52926044 | 528 | } |
9973e9fe | 529 | storeAppendPrintf(sentry, "\n"); |
af00901c | 530 | } |
090089c4 | 531 | |
090089c4 | 532 | /* process objects list */ |
b8d8561b | 533 | void |
534 | stat_ipcache_get(StoreEntry * sentry) | |
090089c4 | 535 | { |
7b04dad5 | 536 | dlink_node *m; |
537 | assert(ip_table != NULL); | |
15576b6a | 538 | storeAppendPrintf(sentry, "IP Cache Statistics:\n"); |
539 | storeAppendPrintf(sentry, "IPcache Entries: %d\n", | |
59c4d35b | 540 | memInUse(MEM_IPCACHE_ENTRY)); |
15576b6a | 541 | storeAppendPrintf(sentry, "IPcache Requests: %d\n", |
30a4f2a8 | 542 | IpcacheStats.requests); |
15576b6a | 543 | storeAppendPrintf(sentry, "IPcache Hits: %d\n", |
30a4f2a8 | 544 | IpcacheStats.hits); |
15576b6a | 545 | storeAppendPrintf(sentry, "IPcache Pending Hits: %d\n", |
30a4f2a8 | 546 | IpcacheStats.pending_hits); |
15576b6a | 547 | storeAppendPrintf(sentry, "IPcache Negative Hits: %d\n", |
30a4f2a8 | 548 | IpcacheStats.negative_hits); |
15576b6a | 549 | storeAppendPrintf(sentry, "IPcache Misses: %d\n", |
30a4f2a8 | 550 | IpcacheStats.misses); |
15576b6a | 551 | storeAppendPrintf(sentry, "Blocking calls to gethostbyname(): %d\n", |
30a4f2a8 | 552 | IpcacheStats.ghbn_calls); |
15576b6a | 553 | storeAppendPrintf(sentry, "Attempts to release locked entries: %d\n", |
429fdbec | 554 | IpcacheStats.release_locked); |
15576b6a | 555 | storeAppendPrintf(sentry, "\n\n"); |
556 | storeAppendPrintf(sentry, "IP Cache Contents:\n\n"); | |
557 | storeAppendPrintf(sentry, " %-29.29s %5s %6s %6s %1s\n", | |
af00901c | 558 | "Hostname", |
559 | "Flags", | |
560 | "lstref", | |
561 | "TTL", | |
562 | "N"); | |
7b04dad5 | 563 | for (m = lru_list.head; m; m = m->next) |
564 | ipcacheStatPrint(m->data, sentry); | |
090089c4 | 565 | } |
4d64d74a | 566 | |
caebbe00 | 567 | static void |
79d39a72 | 568 | dummy_handler(const ipcache_addrs * addrsnotused, void *datanotused) |
30a4f2a8 | 569 | { |
caebbe00 | 570 | return; |
30a4f2a8 | 571 | } |
572 | ||
b8d8561b | 573 | void |
0ee4272b | 574 | ipcacheReleaseInvalid(const char *name) |
877ed73e | 575 | { |
576 | ipcache_entry *i; | |
55906d8e | 577 | if (NULL == name) { |
578 | debug(14, 1) ("ipcacheReleaseInvalid: NULL name\n"); | |
579 | return; | |
580 | } | |
581 | if (0 == strlen(name)) { | |
582 | debug(14, 1) ("ipcacheReleaseInvalid: Empty name\n"); | |
583 | return; | |
584 | } | |
877ed73e | 585 | if ((i = ipcache_get(name)) == NULL) |
586 | return; | |
587 | if (i->status != IP_NEGATIVE_CACHED) | |
588 | return; | |
589 | ipcache_release(i); | |
590 | } | |
f900607e | 591 | |
b8d8561b | 592 | void |
0ee4272b | 593 | ipcacheInvalidate(const char *name) |
f900607e | 594 | { |
595 | ipcache_entry *i; | |
596 | if ((i = ipcache_get(name)) == NULL) | |
597 | return; | |
6c11e193 | 598 | i->expires = squid_curtime; |
599 | /* NOTE, don't call ipcache_release here becuase we might be here due | |
600 | * to a thread started from ipcache_call_pending() which will cause a | |
601 | * FMR */ | |
f900607e | 602 | } |
af00901c | 603 | |
4d650936 | 604 | ipcache_addrs * |
0ee4272b | 605 | ipcacheCheckNumeric(const char *name) |
af00901c | 606 | { |
429fdbec | 607 | struct in_addr ip; |
af00901c | 608 | /* check if it's already a IP address in text form. */ |
429fdbec | 609 | if (!safe_inet_addr(name, &ip)) |
af00901c | 610 | return NULL; |
e5f6c5c2 | 611 | static_addrs.count = 1; |
612 | static_addrs.cur = 0; | |
429fdbec | 613 | static_addrs.in_addrs[0].s_addr = ip.s_addr; |
22c653cd | 614 | static_addrs.bad_mask[0] = FALSE; |
615 | static_addrs.badcount = 0; | |
e5f6c5c2 | 616 | return &static_addrs; |
af00901c | 617 | } |
8905d949 | 618 | |
b8d8561b | 619 | static void |
620 | ipcacheLockEntry(ipcache_entry * i) | |
620da955 | 621 | { |
7b04dad5 | 622 | if (i->locks++ == 0) { |
623 | dlinkDelete(&i->lru, &lru_list); | |
624 | dlinkAdd(i, &i->lru, &lru_list); | |
625 | } | |
620da955 | 626 | } |
627 | ||
b8d8561b | 628 | static void |
629 | ipcacheUnlockEntry(ipcache_entry * i) | |
620da955 | 630 | { |
f889842a | 631 | assert(i->locks > 0); |
620da955 | 632 | i->locks--; |
633 | if (ipcacheExpiredEntry(i)) | |
634 | ipcache_release(i); | |
635 | } | |
e5f6c5c2 | 636 | |
52926044 | 637 | void |
4b4cd312 | 638 | ipcacheCycleAddr(const char *name, ipcache_addrs * ia) |
52926044 | 639 | { |
640 | ipcache_entry *i; | |
641 | unsigned char k; | |
642 | assert(name || ia); | |
643 | if (NULL == ia) { | |
4b4cd312 | 644 | if ((i = ipcache_get(name)) == NULL) |
52926044 | 645 | return; |
4b4cd312 | 646 | if (i->status != IP_CACHED) |
52926044 | 647 | return; |
4b4cd312 | 648 | ia = &i->addrs; |
52926044 | 649 | } |
650 | for (k = 0; k < ia->count; k++) { | |
651 | if (++ia->cur == ia->count) | |
652 | ia->cur = 0; | |
653 | if (!ia->bad_mask[ia->cur]) | |
654 | break;; | |
655 | } | |
656 | if (k == ia->count) { | |
4b4cd312 | 657 | /* All bad, reset to All good */ |
658 | debug(14, 3) ("ipcacheCycleAddr: Changing ALL %s addrs from BAD to OK\n", | |
52926044 | 659 | name); |
4b4cd312 | 660 | for (k = 0; k < ia->count; k++) |
52926044 | 661 | ia->bad_mask[k] = 0; |
4b4cd312 | 662 | ia->badcount = 0; |
663 | ia->cur = 0; | |
52926044 | 664 | } |
4b4cd312 | 665 | debug(14, 3) ("ipcacheCycleAddr: %s now at %s\n", name, |
52926044 | 666 | inet_ntoa(ia->in_addrs[ia->cur])); |
667 | } | |
e5f6c5c2 | 668 | |
52926044 | 669 | /* |
670 | * Marks the given address as BAD and calls ipcacheCycleAddr to | |
671 | * advance the current pointer to the next OK address. | |
22c653cd | 672 | */ |
e5f6c5c2 | 673 | void |
22c653cd | 674 | ipcacheMarkBadAddr(const char *name, struct in_addr addr) |
e5f6c5c2 | 675 | { |
676 | ipcache_entry *i; | |
677 | ipcache_addrs *ia; | |
678 | int k; | |
679 | if ((i = ipcache_get(name)) == NULL) | |
680 | return; | |
681 | ia = &i->addrs; | |
682 | for (k = 0; k < (int) ia->count; k++) { | |
683 | if (ia->in_addrs[k].s_addr == addr.s_addr) | |
684 | break; | |
685 | } | |
52926044 | 686 | if (k == (int) ia->count) /* not found */ |
e5f6c5c2 | 687 | return; |
22c653cd | 688 | if (!ia->bad_mask[k]) { |
689 | ia->bad_mask[k] = TRUE; | |
690 | ia->badcount++; | |
52926044 | 691 | debug(14, 2) ("ipcacheMarkBadAddr: %s [%s]\n", name, inet_ntoa(addr)); |
22c653cd | 692 | } |
52926044 | 693 | ipcacheCycleAddr(name, ia); |
e5f6c5c2 | 694 | } |
56e15c50 | 695 | |
22c653cd | 696 | void |
697 | ipcacheMarkGoodAddr(const char *name, struct in_addr addr) | |
698 | { | |
699 | ipcache_entry *i; | |
700 | ipcache_addrs *ia; | |
701 | int k; | |
702 | if ((i = ipcache_get(name)) == NULL) | |
703 | return; | |
704 | ia = &i->addrs; | |
705 | for (k = 0; k < (int) ia->count; k++) { | |
706 | if (ia->in_addrs[k].s_addr == addr.s_addr) | |
707 | break; | |
708 | } | |
52926044 | 709 | if (k == (int) ia->count) /* not found */ |
22c653cd | 710 | return; |
52926044 | 711 | if (!ia->bad_mask[k]) /* already OK */ |
712 | return; | |
713 | ia->bad_mask[k] = FALSE; | |
714 | ia->badcount--; | |
715 | debug(14, 2) ("ipcacheMarkGoodAddr: %s [%s]\n", name, inet_ntoa(addr)); | |
22c653cd | 716 | } |
717 | ||
ec878047 | 718 | static void |
719 | ipcacheFreeEntry(void *data) | |
720 | { | |
721 | ipcache_entry *i = data; | |
0e1a4726 | 722 | ip_pending *p; |
723 | while ((p = i->pending_head)) { | |
724 | i->pending_head = p->next; | |
725 | memFree(MEM_IPCACHE_PENDING, p); | |
726 | } | |
ec878047 | 727 | safe_free(i->addrs.in_addrs); |
728 | safe_free(i->addrs.bad_mask); | |
729 | safe_free(i->name); | |
730 | safe_free(i->error_message); | |
731 | memFree(MEM_IPCACHE_ENTRY, i); | |
732 | } | |
733 | ||
56e15c50 | 734 | void |
735 | ipcacheFreeMemory(void) | |
736 | { | |
ec878047 | 737 | hashFreeItems(ip_table, ipcacheFreeEntry); |
56e15c50 | 738 | hashFreeMemory(ip_table); |
afe95a7e | 739 | ip_table = NULL; |
56e15c50 | 740 | } |
3fb036e8 | 741 | |
742 | static void | |
743 | ipcacheChangeKey(ipcache_entry * i) | |
744 | { | |
745 | static int index = 0; | |
746 | LOCAL_ARRAY(char, new_key, 256); | |
747 | hash_link *table_entry = hash_lookup(ip_table, i->name); | |
748 | if (table_entry == NULL) { | |
a3d5953d | 749 | debug(14, 0) ("ipcacheChangeKey: Could not find key '%s'\n", i->name); |
3fb036e8 | 750 | return; |
751 | } | |
693e7ec9 | 752 | assert(i == (ipcache_entry *) table_entry); |
3fda0827 | 753 | hash_remove_link(ip_table, table_entry); |
137ee196 | 754 | snprintf(new_key, 256, "%d/%s", ++index, i->name); |
a3d5953d | 755 | debug(14, 1) ("ipcacheChangeKey: from '%s' to '%s'\n", i->name, new_key); |
3fb036e8 | 756 | safe_free(i->name); |
757 | i->name = xstrdup(new_key); | |
7b04dad5 | 758 | hash_join(ip_table, (hash_link *) i); |
3fb036e8 | 759 | } |
429fdbec | 760 | |
761 | /* call during reconfigure phase to clear out all the | |
762 | * pending and dispatched reqeusts that got lost */ | |
763 | void | |
764 | ipcache_restart(void) | |
765 | { | |
766 | ipcache_entry *this; | |
76a5501f | 767 | assert(ip_table != NULL); |
0f6bebac | 768 | hash_first(ip_table); |
769 | while ((this = (ipcache_entry *) hash_next(ip_table))) { | |
429fdbec | 770 | if (this->status == IP_CACHED) |
771 | continue; | |
772 | if (this->status == IP_NEGATIVE_CACHED) | |
773 | continue; | |
429fdbec | 774 | } |
775 | /* recalculate these while we're at it */ | |
776 | ipcache_high = (long) (((float) Config.ipcache.size * | |
777 | (float) Config.ipcache.high) / (float) 100); | |
778 | ipcache_low = (long) (((float) Config.ipcache.size * | |
779 | (float) Config.ipcache.low) / (float) 100); | |
780 | } | |
ce75f381 | 781 | |
782 | #ifdef SQUID_SNMP | |
1f5b542b | 783 | /* |
135171fe | 784 | * The function to return the ip cache statistics to via SNMP |
785 | */ | |
164f7660 | 786 | |
86115da5 | 787 | variable_list * |
1f5b542b | 788 | snmp_netIpFn(variable_list * Var, snint * ErrP) |
d60c11be | 789 | { |
86115da5 | 790 | variable_list *Answer; |
86115da5 | 791 | |
1f5b542b | 792 | debug(49, 5) ("snmp_netIpFn: Processing request:\n", Var->name[LEN_SQ_NET + 1]); |
793 | snmpDebugOid(5, Var->name, Var->name_length); | |
86115da5 | 794 | |
86115da5 | 795 | Answer = snmp_var_new(Var->name, Var->name_length); |
796 | *ErrP = SNMP_ERR_NOERROR; | |
c6da280c | 797 | Answer->val_len = sizeof(snint); |
451b07c5 | 798 | Answer->val.integer = xmalloc(Answer->val_len); |
1f5b542b | 799 | Answer->type = SMI_COUNTER32; |
800 | ||
135171fe | 801 | switch (Var->name[LEN_SQ_NET + 1]) { |
1f5b542b | 802 | case IP_ENT: |
803 | *(Answer->val.integer) = memInUse(MEM_IPCACHE_ENTRY); | |
804 | Answer->type = SMI_GAUGE32; | |
135171fe | 805 | break; |
1f5b542b | 806 | case IP_REQ: |
807 | *(Answer->val.integer) = IpcacheStats.requests; | |
135171fe | 808 | break; |
1f5b542b | 809 | case IP_HITS: |
810 | *(Answer->val.integer) = IpcacheStats.hits; | |
135171fe | 811 | break; |
1f5b542b | 812 | case IP_PENDHIT: |
813 | *(Answer->val.integer) = IpcacheStats.pending_hits; | |
814 | Answer->type = SMI_GAUGE32; | |
135171fe | 815 | break; |
1f5b542b | 816 | case IP_NEGHIT: |
817 | *(Answer->val.integer) = IpcacheStats.negative_hits; | |
135171fe | 818 | break; |
1f5b542b | 819 | case IP_MISS: |
820 | *(Answer->val.integer) = IpcacheStats.misses; | |
135171fe | 821 | break; |
1f5b542b | 822 | case IP_GHBN: |
823 | *(Answer->val.integer) = IpcacheStats.ghbn_calls; | |
135171fe | 824 | break; |
1f5b542b | 825 | case IP_LOC: |
826 | *(Answer->val.integer) = IpcacheStats.release_locked; | |
135171fe | 827 | break; |
74addf6c | 828 | #if DELETE_ME |
1f5b542b | 829 | case IP_LENG: |
830 | *(Answer->val.integer) = queue_length; | |
831 | Answer->type = SMI_GAUGE32; | |
135171fe | 832 | break; |
74addf6c | 833 | #endif |
ce75f381 | 834 | default: |
135171fe | 835 | *ErrP = SNMP_ERR_NOSUCHNAME; |
836 | snmp_var_free(Answer); | |
837 | return (NULL); | |
86115da5 | 838 | } |
839 | return Answer; | |
ce75f381 | 840 | } |
1f5b542b | 841 | |
135171fe | 842 | #endif /*SQUID_SNMP */ |