]>
Commit | Line | Data |
---|---|---|
f88bb09c | 1 | |
5999b776 | 2 | |
f88bb09c | 3 | /* |
2094fc54 | 4 | * $Id: fqdncache.cc,v 1.130 1999/06/17 20:23:11 wessels Exp $ |
f88bb09c | 5 | * |
7cf620a9 | 6 | * DEBUG: section 35 FQDN Cache |
f88bb09c | 7 | * AUTHOR: Harvest Derived |
8 | * | |
42c04c16 | 9 | * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ |
e25c139f | 10 | * ---------------------------------------------------------- |
f88bb09c | 11 | * |
12 | * Squid is the result of efforts by numerous individuals from the | |
13 | * Internet community. Development is led by Duane Wessels of the | |
e25c139f | 14 | * National Laboratory for Applied Network Research and funded by the |
15 | * National Science Foundation. Squid is Copyrighted (C) 1998 by | |
16 | * Duane Wessels and the University of California San Diego. Please | |
17 | * see the COPYRIGHT file for full details. Squid incorporates | |
18 | * software developed and/or copyrighted by other sources. Please see | |
19 | * the CREDITS file for full details. | |
f88bb09c | 20 | * |
21 | * This program is free software; you can redistribute it and/or modify | |
22 | * it under the terms of the GNU General Public License as published by | |
23 | * the Free Software Foundation; either version 2 of the License, or | |
24 | * (at your option) any later version. | |
25 | * | |
26 | * This program is distributed in the hope that it will be useful, | |
27 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
28 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
29 | * GNU General Public License for more details. | |
30 | * | |
31 | * You should have received a copy of the GNU General Public License | |
32 | * along with this program; if not, write to the Free Software | |
cbdec147 | 33 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. |
e25c139f | 34 | * |
f88bb09c | 35 | */ |
36 | ||
37 | #include "squid.h" | |
38 | ||
f88bb09c | 39 | #define FQDN_LOW_WATER 90 |
40 | #define FQDN_HIGH_WATER 95 | |
f88bb09c | 41 | |
f88bb09c | 42 | static struct { |
43 | int requests; | |
44 | int replies; | |
45 | int hits; | |
46 | int misses; | |
47 | int pending_hits; | |
48 | int negative_hits; | |
49 | int errors; | |
f88bb09c | 50 | int ghba_calls; /* # calls to blocking gethostbyaddr() */ |
51 | } FqdncacheStats; | |
52 | ||
4bc76d59 | 53 | static dlink_list lru_list; |
54 | ||
59f34d62 | 55 | #if USE_DNSSERVERS |
74addf6c | 56 | static HLPCB fqdncacheHandleReply; |
57 | static fqdncache_entry *fqdncacheParse(const char *buf); | |
59f34d62 | 58 | #else |
59 | static IDNSCB fqdncacheHandleReply; | |
60 | static fqdncache_entry *fqdncacheParse(rfc1035_rr *, int); | |
61 | #endif | |
f5b8bbc4 | 62 | static void fqdncache_release(fqdncache_entry *); |
f5b8bbc4 | 63 | static fqdncache_entry *fqdncache_create(const char *name); |
f5b8bbc4 | 64 | static void fqdncache_call_pending(fqdncache_entry *); |
65 | static void fqdncacheAddHostent(fqdncache_entry *, const struct hostent *); | |
f5b8bbc4 | 66 | static fqdncache_entry *fqdncache_get(const char *); |
348b2031 | 67 | static FQDNH dummy_handler; |
f5b8bbc4 | 68 | static int fqdncacheExpiredEntry(const fqdncache_entry *); |
69 | static void fqdncacheAddPending(fqdncache_entry *, FQDNH *, void *); | |
f5b8bbc4 | 70 | static void fqdncacheChangeKey(fqdncache_entry * i); |
71 | static void fqdncacheLockEntry(fqdncache_entry * f); | |
72 | static void fqdncacheUnlockEntry(fqdncache_entry * f); | |
ec878047 | 73 | static FREE fqdncacheFreeEntry; |
f88bb09c | 74 | |
365e5b34 | 75 | static hash_table *fqdn_table = NULL; |
f88bb09c | 76 | |
77 | static char fqdncache_status_char[] = | |
78 | { | |
79 | 'C', | |
80 | 'N', | |
81 | 'P', | |
82 | 'D' | |
83 | }; | |
84 | ||
24382924 | 85 | static long fqdncache_low = 180; |
86 | static long fqdncache_high = 200; | |
f88bb09c | 87 | |
f88bb09c | 88 | /* removes the given fqdncache entry */ |
b8d8561b | 89 | static void |
90 | fqdncache_release(fqdncache_entry * f) | |
f88bb09c | 91 | { |
f88bb09c | 92 | int k; |
e144eae4 | 93 | assert(f->status != FQDN_PENDING); |
94 | assert(f->status != FQDN_DISPATCHED); | |
95 | assert(f->pending_head == NULL); | |
3fda0827 | 96 | hash_remove_link(fqdn_table, (hash_link *) f); |
429fdbec | 97 | if (f->status == FQDN_CACHED) { |
f88bb09c | 98 | for (k = 0; k < (int) f->name_count; k++) |
99 | safe_free(f->names[k]); | |
a3d5953d | 100 | debug(35, 5) ("fqdncache_release: Released FQDN record for '%s'.\n", |
429fdbec | 101 | f->name); |
f88bb09c | 102 | } |
4bc76d59 | 103 | dlinkDelete(&f->lru, &lru_list); |
429fdbec | 104 | safe_free(f->name); |
105 | safe_free(f->error_message); | |
db1cd23c | 106 | memFree(f, MEM_FQDNCACHE_ENTRY); |
f88bb09c | 107 | } |
108 | ||
109 | /* return match for given name */ | |
b8d8561b | 110 | static fqdncache_entry * |
0ee4272b | 111 | fqdncache_get(const char *name) |
f88bb09c | 112 | { |
113 | hash_link *e; | |
114 | static fqdncache_entry *f; | |
f88bb09c | 115 | f = NULL; |
116 | if (fqdn_table) { | |
117 | if ((e = hash_lookup(fqdn_table, name)) != NULL) | |
118 | f = (fqdncache_entry *) e; | |
119 | } | |
120 | return f; | |
121 | } | |
122 | ||
b8d8561b | 123 | static int |
fe4e214f | 124 | fqdncacheExpiredEntry(const fqdncache_entry * f) |
f88bb09c | 125 | { |
126 | if (f->status == FQDN_PENDING) | |
127 | return 0; | |
128 | if (f->status == FQDN_DISPATCHED) | |
129 | return 0; | |
429fdbec | 130 | if (f->locks != 0) |
131 | return 0; | |
e84703ad | 132 | if (f->expires > squid_curtime) |
f88bb09c | 133 | return 0; |
134 | return 1; | |
135 | } | |
136 | ||
59c4d35b | 137 | void |
138 | fqdncache_purgelru(void *notused) | |
f88bb09c | 139 | { |
4bc76d59 | 140 | dlink_node *m; |
141 | dlink_node *prev = NULL; | |
142 | fqdncache_entry *f; | |
f88bb09c | 143 | int removed = 0; |
52040193 | 144 | eventAdd("fqdncache_purgelru", fqdncache_purgelru, NULL, 10.0, 1); |
4bc76d59 | 145 | for (m = lru_list.tail; m; m = prev) { |
59c4d35b | 146 | if (memInUse(MEM_FQDNCACHE_ENTRY) < fqdncache_low) |
4bc76d59 | 147 | break; |
148 | prev = m->prev; | |
149 | f = m->data; | |
f88bb09c | 150 | if (f->status == FQDN_PENDING) |
151 | continue; | |
152 | if (f->status == FQDN_DISPATCHED) | |
153 | continue; | |
4bc76d59 | 154 | if (f->locks != 0) |
155 | continue; | |
156 | fqdncache_release(f); | |
f88bb09c | 157 | removed++; |
158 | } | |
ac750329 | 159 | debug(35, 9) ("fqdncache_purgelru: removed %d entries\n", removed); |
f88bb09c | 160 | } |
161 | ||
f88bb09c | 162 | /* create blank fqdncache_entry */ |
b8d8561b | 163 | static fqdncache_entry * |
429fdbec | 164 | fqdncache_create(const char *name) |
f88bb09c | 165 | { |
4bc76d59 | 166 | static fqdncache_entry *f; |
59c4d35b | 167 | f = memAllocate(MEM_FQDNCACHE_ENTRY); |
4bc76d59 | 168 | f->name = xstrdup(name); |
169 | f->expires = squid_curtime + Config.negativeDnsTtl; | |
7db83ee8 | 170 | hash_join(fqdn_table, (hash_link *) f); |
4bc76d59 | 171 | dlinkAdd(f, &f->lru, &lru_list); |
172 | return f; | |
f88bb09c | 173 | } |
174 | ||
b8d8561b | 175 | static void |
429fdbec | 176 | fqdncacheAddHostent(fqdncache_entry * f, const struct hostent *hp) |
f88bb09c | 177 | { |
178 | int k; | |
429fdbec | 179 | f->name_count = 0; |
180 | f->names[f->name_count++] = xstrdup((char *) hp->h_name); | |
181 | for (k = 0; hp->h_aliases[k]; k++) { | |
182 | f->names[f->name_count++] = xstrdup(hp->h_aliases[k]); | |
183 | if (f->name_count == FQDN_MAX_NAMES) | |
184 | break; | |
185 | } | |
186 | } | |
f88bb09c | 187 | |
429fdbec | 188 | static fqdncache_entry * |
189 | fqdncacheAddNew(const char *name, const struct hostent *hp, fqdncache_status_t status) | |
190 | { | |
191 | fqdncache_entry *f; | |
e144eae4 | 192 | assert(fqdncache_get(name) == NULL); |
6a78c18e | 193 | debug(35, 10) ("fqdncacheAddNew: Adding '%s', status=%c\n", |
429fdbec | 194 | name, |
195 | fqdncache_status_char[status]); | |
196 | f = fqdncache_create(name); | |
197 | if (hp) | |
198 | fqdncacheAddHostent(f, hp); | |
199 | f->status = status; | |
200 | f->lastref = squid_curtime; | |
201 | return f; | |
f88bb09c | 202 | } |
203 | ||
204 | /* walks down the pending list, calling handlers */ | |
b8d8561b | 205 | static void |
206 | fqdncache_call_pending(fqdncache_entry * f) | |
f88bb09c | 207 | { |
59c4d35b | 208 | fqdn_pending *p = NULL; |
f88bb09c | 209 | int nhandler = 0; |
f88bb09c | 210 | f->lastref = squid_curtime; |
429fdbec | 211 | fqdncacheLockEntry(f); |
f88bb09c | 212 | while (f->pending_head != NULL) { |
213 | p = f->pending_head; | |
214 | f->pending_head = p->next; | |
215 | if (p->handler) { | |
216 | nhandler++; | |
217 | dns_error_message = f->error_message; | |
348b2031 | 218 | p->handler((f->status == FQDN_CACHED) ? f->names[0] : NULL, |
f88bb09c | 219 | p->handlerData); |
220 | } | |
db1cd23c | 221 | memFree(p, MEM_FQDNCACHE_PENDING); |
f88bb09c | 222 | } |
223 | f->pending_head = NULL; /* nuke list */ | |
a3d5953d | 224 | debug(35, 10) ("fqdncache_call_pending: Called %d handlers.\n", nhandler); |
429fdbec | 225 | fqdncacheUnlockEntry(f); |
f88bb09c | 226 | } |
227 | ||
b8d8561b | 228 | static fqdncache_entry * |
b18baa48 | 229 | #if USE_DNSSERVERS |
74addf6c | 230 | fqdncacheParse(const char *inbuf) |
f88bb09c | 231 | { |
bd34f258 | 232 | LOCAL_ARRAY(char, buf, DNS_INBUF_SZ); |
e84703ad | 233 | char *token; |
234 | static fqdncache_entry f; | |
bd34f258 | 235 | int ttl; |
236 | xstrncpy(buf, inbuf, DNS_INBUF_SZ); | |
778f4e28 | 237 | debug(35, 5) ("fqdncacheParse: parsing: {%s}\n", buf); |
bd34f258 | 238 | memset(&f, '\0', sizeof(f)); |
239 | f.expires = squid_curtime; | |
240 | f.status = FQDN_NEGATIVE_CACHED; | |
c68e9c6b | 241 | if (inbuf == NULL) { |
242 | debug(35, 1) ("fqdncacheParse: Got <NULL> reply\n"); | |
243 | return &f; | |
244 | } | |
bd34f258 | 245 | token = strtok(buf, w_space); |
246 | if (NULL == token) { | |
6a78c18e | 247 | debug(35, 1) ("fqdncacheParse: Got <NULL>, expecting '$name'\n"); |
bd34f258 | 248 | return &f; |
249 | } | |
250 | if (0 == strcmp(token, "$fail")) { | |
251 | f.expires = squid_curtime + Config.negativeDnsTtl; | |
252 | token = strtok(NULL, "\n"); | |
253 | assert(NULL != token); | |
254 | f.error_message = xstrdup(token); | |
255 | return &f; | |
256 | } | |
257 | if (0 != strcmp(token, "$name")) { | |
6a78c18e | 258 | debug(35, 1) ("fqdncacheParse: Got '%s', expecting '$name'\n", token); |
bd34f258 | 259 | return &f; |
260 | } | |
261 | token = strtok(NULL, w_space); | |
262 | if (NULL == token) { | |
6a78c18e | 263 | debug(35, 1) ("fqdncacheParse: Got <NULL>, expecting TTL\n"); |
bd34f258 | 264 | return &f; |
265 | } | |
266 | f.status = FQDN_CACHED; | |
267 | ttl = atoi(token); | |
268 | if (ttl > 0) | |
269 | f.expires = squid_curtime + ttl; | |
270 | else | |
271 | f.expires = squid_curtime + Config.positiveDnsTtl; | |
272 | token = strtok(NULL, w_space); | |
273 | if (NULL != token) { | |
274 | f.names[0] = xstrdup(token); | |
275 | f.name_count = 1; | |
f88bb09c | 276 | } |
e84703ad | 277 | return &f; |
f88bb09c | 278 | } |
59f34d62 | 279 | #else |
59f34d62 | 280 | fqdncacheParse(rfc1035_rr * answers, int nr) |
281 | { | |
282 | static fqdncache_entry f; | |
283 | int k; | |
284 | int j; | |
285 | int na = 0; | |
286 | memset(&f, '\0', sizeof(f)); | |
287 | f.expires = squid_curtime; | |
288 | f.status = FQDN_NEGATIVE_CACHED; | |
289 | if (nr < 0) { | |
b18baa48 | 290 | debug(35, 3) ("fqdncacheParse: Lookup failed (error %d)\n", |
59f34d62 | 291 | rfc1035_errno); |
292 | assert(rfc1035_error_message); | |
293 | f.error_message = xstrdup(rfc1035_error_message); | |
294 | return &f; | |
295 | } | |
296 | if (nr == 0) { | |
b18baa48 | 297 | debug(35, 3) ("fqdncacheParse: No DNS records\n"); |
59f34d62 | 298 | f.error_message = xstrdup("No DNS records"); |
299 | return &f; | |
300 | } | |
39d5df9b | 301 | debug(35, 3) ("fqdncacheParse: %d answers\n", nr); |
59f34d62 | 302 | assert(answers); |
303 | for (j = 0, k = 0; k < nr; k++) { | |
304 | if (answers[k].type != RFC1035_TYPE_PTR) | |
305 | continue; | |
306 | if (answers[k].class != RFC1035_CLASS_IN) | |
307 | continue; | |
308 | na++; | |
309 | f.status = FQDN_CACHED; | |
310 | f.names[0] = xstrdup(answers[k].rdata); | |
311 | f.name_count = 1; | |
312 | f.expires = squid_curtime + answers[k].ttl; | |
313 | return &f; | |
314 | } | |
315 | debug(35, 1) ("fqdncacheParse: No PTR record\n"); | |
316 | f.error_message = xstrdup("No PTR record"); | |
317 | return &f; | |
318 | } | |
319 | #endif | |
f88bb09c | 320 | |
429fdbec | 321 | static void |
59f34d62 | 322 | #if USE_DNSSERVERS |
74addf6c | 323 | fqdncacheHandleReply(void *data, char *reply) |
59f34d62 | 324 | #else |
325 | fqdncacheHandleReply(void *data, rfc1035_rr * answers, int na) | |
326 | #endif | |
f88bb09c | 327 | { |
f88bb09c | 328 | int n; |
74addf6c | 329 | generic_cbdata *c = data; |
330 | fqdncache_entry *f = c->data; | |
e84703ad | 331 | fqdncache_entry *x = NULL; |
698e6f16 | 332 | assert(f->status == FQDN_DISPATCHED); |
74addf6c | 333 | assert(f->locks); |
334 | cbdataFree(c); | |
335 | c = NULL; | |
c68e9c6b | 336 | n = ++FqdncacheStats.replies; |
74addf6c | 337 | statHistCount(&Counter.dns.svc_time, |
338 | tvSubMsec(f->request_time, current_time)); | |
59f34d62 | 339 | #if USE_DNSSERVERS |
c68e9c6b | 340 | x = fqdncacheParse(reply); |
59f34d62 | 341 | #else |
342 | x = fqdncacheParse(answers, na); | |
343 | #endif | |
c68e9c6b | 344 | assert(x); |
345 | f->name_count = x->name_count; | |
346 | for (n = 0; n < (int) f->name_count; n++) | |
347 | f->names[n] = x->names[n]; | |
348 | f->error_message = x->error_message; | |
349 | f->status = x->status; | |
350 | f->expires = x->expires; | |
351 | fqdncache_call_pending(f); | |
74addf6c | 352 | fqdncacheUnlockEntry(f); /* unlock from FQDN_DISPATCHED */ |
f88bb09c | 353 | } |
354 | ||
b8d8561b | 355 | static void |
348b2031 | 356 | fqdncacheAddPending(fqdncache_entry * f, FQDNH * handler, void *handlerData) |
f88bb09c | 357 | { |
59c4d35b | 358 | fqdn_pending *pending = memAllocate(MEM_FQDNCACHE_PENDING); |
359 | fqdn_pending **I = NULL; | |
429fdbec | 360 | f->lastref = squid_curtime; |
f88bb09c | 361 | pending->handler = handler; |
362 | pending->handlerData = handlerData; | |
f88bb09c | 363 | for (I = &(f->pending_head); *I; I = &((*I)->next)); |
364 | *I = pending; | |
365 | } | |
366 | ||
429fdbec | 367 | void |
348b2031 | 368 | fqdncache_nbgethostbyaddr(struct in_addr addr, FQDNH * handler, void *handlerData) |
f88bb09c | 369 | { |
370 | fqdncache_entry *f = NULL; | |
f88bb09c | 371 | char *name = inet_ntoa(addr); |
74addf6c | 372 | generic_cbdata *c; |
698e6f16 | 373 | assert(handler); |
a3d5953d | 374 | debug(35, 4) ("fqdncache_nbgethostbyaddr: Name '%s'.\n", name); |
f88bb09c | 375 | FqdncacheStats.requests++; |
f88bb09c | 376 | if (name == NULL || name[0] == '\0') { |
a3d5953d | 377 | debug(35, 4) ("fqdncache_nbgethostbyaddr: Invalid name!\n"); |
348b2031 | 378 | handler(NULL, handlerData); |
429fdbec | 379 | return; |
f88bb09c | 380 | } |
381 | if ((f = fqdncache_get(name))) { | |
382 | if (fqdncacheExpiredEntry(f)) { | |
383 | fqdncache_release(f); | |
384 | f = NULL; | |
385 | } | |
386 | } | |
387 | if (f == NULL) { | |
388 | /* MISS: No entry, create the new one */ | |
a3d5953d | 389 | debug(35, 5) ("fqdncache_nbgethostbyaddr: MISS for '%s'\n", name); |
f88bb09c | 390 | FqdncacheStats.misses++; |
429fdbec | 391 | f = fqdncacheAddNew(name, NULL, FQDN_PENDING); |
348b2031 | 392 | fqdncacheAddPending(f, handler, handlerData); |
74addf6c | 393 | f->request_time = current_time; |
f88bb09c | 394 | } else if (f->status == FQDN_CACHED || f->status == FQDN_NEGATIVE_CACHED) { |
395 | /* HIT */ | |
a3d5953d | 396 | debug(35, 4) ("fqdncache_nbgethostbyaddr: HIT for '%s'\n", name); |
f88bb09c | 397 | if (f->status == FQDN_NEGATIVE_CACHED) |
398 | FqdncacheStats.negative_hits++; | |
399 | else | |
400 | FqdncacheStats.hits++; | |
348b2031 | 401 | fqdncacheAddPending(f, handler, handlerData); |
f88bb09c | 402 | fqdncache_call_pending(f); |
429fdbec | 403 | return; |
f88bb09c | 404 | } else if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) { |
a3d5953d | 405 | debug(35, 4) ("fqdncache_nbgethostbyaddr: PENDING for '%s'\n", name); |
f88bb09c | 406 | FqdncacheStats.pending_hits++; |
348b2031 | 407 | fqdncacheAddPending(f, handler, handlerData); |
429fdbec | 408 | if (squid_curtime - f->expires > 600) { |
6a78c18e | 409 | debug(35, 0) ("fqdncache_nbgethostbyname: '%s' PENDING for %d seconds, aborting\n", name, |
ec878047 | 410 | (int) (squid_curtime + Config.negativeDnsTtl - f->expires)); |
429fdbec | 411 | fqdncacheChangeKey(f); |
412 | fqdncache_call_pending(f); | |
413 | } | |
414 | return; | |
f88bb09c | 415 | } else { |
d46a87a8 | 416 | debug(35, 1) ("fqdncache_nbgethostbyaddr: BAD status %d", |
417 | (int) f->status); | |
698e6f16 | 418 | assert(0); |
f88bb09c | 419 | } |
74addf6c | 420 | /* for HIT, PENDING, DISPATCHED we've returned. For MISS we submit */ |
421 | c = xcalloc(1, sizeof(*c)); | |
422 | c->data = f; | |
db1cd23c | 423 | cbdataAdd(c, cbdataXfree, 0); |
429fdbec | 424 | f->status = FQDN_DISPATCHED; |
4bc76d59 | 425 | fqdncacheLockEntry(f); /* lock while FQDN_DISPATCHED */ |
59f34d62 | 426 | #if USE_DNSSERVERS |
74addf6c | 427 | dnsSubmit(f->name, fqdncacheHandleReply, c); |
59f34d62 | 428 | #else |
429 | idnsPTRLookup(addr, fqdncacheHandleReply, c); | |
430 | #endif | |
f88bb09c | 431 | } |
432 | ||
f88bb09c | 433 | /* initialize the fqdncache */ |
b8d8561b | 434 | void |
0673c0ba | 435 | fqdncache_init(void) |
f88bb09c | 436 | { |
aa9e2cab | 437 | int n; |
19054954 | 438 | if (fqdn_table) |
439 | return; | |
a3d5953d | 440 | debug(35, 3) ("Initializing FQDN Cache...\n"); |
f88bb09c | 441 | memset(&FqdncacheStats, '\0', sizeof(FqdncacheStats)); |
3eb55834 | 442 | memset(&lru_list, '\0', sizeof(lru_list)); |
e55650e3 | 443 | fqdncache_high = (long) (((float) Config.fqdncache.size * |
f88bb09c | 444 | (float) FQDN_HIGH_WATER) / (float) 100); |
e55650e3 | 445 | fqdncache_low = (long) (((float) Config.fqdncache.size * |
f88bb09c | 446 | (float) FQDN_LOW_WATER) / (float) 100); |
aa9e2cab | 447 | n = hashPrime(fqdncache_high / 4); |
6a78c18e | 448 | fqdn_table = hash_create((HASHCMP *) strcmp, n, hash4); |
22f3fd98 | 449 | cachemgrRegister("fqdncache", |
450 | "FQDN Cache Stats and Contents", | |
1da3b90b | 451 | fqdnStats, 0, 1); |
f88bb09c | 452 | } |
453 | ||
454 | /* clean up the pending entries in dnsserver */ | |
455 | /* return 1 if we found the host, 0 otherwise */ | |
b8d8561b | 456 | int |
b69f7771 | 457 | fqdncacheUnregister(struct in_addr addr, void *data) |
f88bb09c | 458 | { |
03047798 | 459 | char *name = inet_ntoa(addr); |
f88bb09c | 460 | fqdncache_entry *f = NULL; |
59c4d35b | 461 | fqdn_pending *p = NULL; |
f88bb09c | 462 | int n = 0; |
5a675d84 | 463 | debug(35, 3) ("fqdncacheUnregister: name '%s'\n", name); |
f88bb09c | 464 | if ((f = fqdncache_get(name)) == NULL) |
465 | return 0; | |
466 | if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) { | |
467 | for (p = f->pending_head; p; p = p->next) { | |
b69f7771 | 468 | if (p->handlerData != data) |
469 | continue; | |
470 | p->handler = NULL; | |
b69f7771 | 471 | n++; |
f88bb09c | 472 | } |
473 | } | |
b69f7771 | 474 | if (n == 0) |
475 | debug_trap("fqdncacheUnregister: callback data not found"); | |
a3d5953d | 476 | debug(35, 3) ("fqdncacheUnregister: unregistered %d handlers\n", n); |
f88bb09c | 477 | return n; |
478 | } | |
479 | ||
0ee4272b | 480 | const char * |
b8d8561b | 481 | fqdncache_gethostbyaddr(struct in_addr addr, int flags) |
f88bb09c | 482 | { |
483 | char *name = inet_ntoa(addr); | |
484 | fqdncache_entry *f = NULL; | |
429fdbec | 485 | struct in_addr ip; |
698e6f16 | 486 | assert(name); |
f88bb09c | 487 | FqdncacheStats.requests++; |
488 | if ((f = fqdncache_get(name))) { | |
429fdbec | 489 | if (fqdncacheExpiredEntry(f)) { |
490 | fqdncache_release(f); | |
491 | f = NULL; | |
492 | } | |
493 | } | |
494 | if (f) { | |
7c63efed | 495 | if (f->status == FQDN_NEGATIVE_CACHED) { |
f88bb09c | 496 | FqdncacheStats.negative_hits++; |
497 | dns_error_message = f->error_message; | |
498 | return NULL; | |
499 | } else { | |
500 | FqdncacheStats.hits++; | |
501 | f->lastref = squid_curtime; | |
502 | return f->names[0]; | |
503 | } | |
504 | } | |
f88bb09c | 505 | /* check if it's already a FQDN address in text form. */ |
429fdbec | 506 | if (!safe_inet_addr(name, &ip)) |
f88bb09c | 507 | return name; |
429fdbec | 508 | FqdncacheStats.misses++; |
f88bb09c | 509 | if (flags & FQDN_LOOKUP_IF_MISS) |
348b2031 | 510 | fqdncache_nbgethostbyaddr(addr, dummy_handler, NULL); |
f88bb09c | 511 | return NULL; |
512 | } | |
513 | ||
514 | ||
515 | /* process objects list */ | |
b8d8561b | 516 | void |
517 | fqdnStats(StoreEntry * sentry) | |
f88bb09c | 518 | { |
519 | fqdncache_entry *f = NULL; | |
520 | int k; | |
521 | int ttl; | |
4bc76d59 | 522 | if (fqdn_table == NULL) |
f88bb09c | 523 | return; |
15576b6a | 524 | storeAppendPrintf(sentry, "FQDN Cache Statistics:\n"); |
525 | storeAppendPrintf(sentry, "FQDNcache Entries: %d\n", | |
59c4d35b | 526 | memInUse(MEM_FQDNCACHE_ENTRY)); |
15576b6a | 527 | storeAppendPrintf(sentry, "FQDNcache Requests: %d\n", |
f88bb09c | 528 | FqdncacheStats.requests); |
15576b6a | 529 | storeAppendPrintf(sentry, "FQDNcache Hits: %d\n", |
f88bb09c | 530 | FqdncacheStats.hits); |
15576b6a | 531 | storeAppendPrintf(sentry, "FQDNcache Pending Hits: %d\n", |
f88bb09c | 532 | FqdncacheStats.pending_hits); |
15576b6a | 533 | storeAppendPrintf(sentry, "FQDNcache Negative Hits: %d\n", |
f88bb09c | 534 | FqdncacheStats.negative_hits); |
15576b6a | 535 | storeAppendPrintf(sentry, "FQDNcache Misses: %d\n", |
f88bb09c | 536 | FqdncacheStats.misses); |
15576b6a | 537 | storeAppendPrintf(sentry, "Blocking calls to gethostbyaddr(): %d\n", |
f88bb09c | 538 | FqdncacheStats.ghba_calls); |
15576b6a | 539 | storeAppendPrintf(sentry, "FQDN Cache Contents:\n\n"); |
f88bb09c | 540 | |
0f6bebac | 541 | hash_first(fqdn_table); |
542 | while ((f = (fqdncache_entry *) hash_next(fqdn_table))) { | |
f88bb09c | 543 | if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) |
544 | ttl = 0; | |
545 | else | |
e84703ad | 546 | ttl = (f->expires - squid_curtime); |
15576b6a | 547 | storeAppendPrintf(sentry, " %-32.32s %c %6d %d", |
f88bb09c | 548 | f->name, |
549 | fqdncache_status_char[f->status], | |
550 | ttl, | |
551 | (int) f->name_count); | |
552 | for (k = 0; k < (int) f->name_count; k++) | |
553 | storeAppendPrintf(sentry, " %s", f->names[k]); | |
22f3fd98 | 554 | storeAppendPrintf(sentry, "\n"); |
f88bb09c | 555 | } |
f88bb09c | 556 | } |
557 | ||
b8d8561b | 558 | static void |
79d39a72 | 559 | dummy_handler(const char *bufnotused, void *datanotused) |
f88bb09c | 560 | { |
561 | return; | |
562 | } | |
563 | ||
b8d8561b | 564 | void |
0ee4272b | 565 | fqdncacheReleaseInvalid(const char *name) |
f88bb09c | 566 | { |
567 | fqdncache_entry *f; | |
568 | if ((f = fqdncache_get(name)) == NULL) | |
569 | return; | |
570 | if (f->status != FQDN_NEGATIVE_CACHED) | |
571 | return; | |
572 | fqdncache_release(f); | |
573 | } | |
28ab0c0a | 574 | |
0ee4272b | 575 | const char * |
b8d8561b | 576 | fqdnFromAddr(struct in_addr addr) |
28ab0c0a | 577 | { |
0ee4272b | 578 | const char *n; |
39de381a | 579 | static char buf[32]; |
17a0a4ee | 580 | if (Config.onoff.log_fqdn && (n = fqdncache_gethostbyaddr(addr, 0))) |
28ab0c0a | 581 | return n; |
f2052513 | 582 | xstrncpy(buf, inet_ntoa(addr), 32); |
39de381a | 583 | return buf; |
28ab0c0a | 584 | } |
f201f309 | 585 | |
429fdbec | 586 | static void |
587 | fqdncacheLockEntry(fqdncache_entry * f) | |
588 | { | |
4bc76d59 | 589 | if (f->locks++ == 0) { |
590 | dlinkDelete(&f->lru, &lru_list); | |
591 | dlinkAdd(f, &f->lru, &lru_list); | |
592 | } | |
429fdbec | 593 | } |
594 | ||
595 | static void | |
596 | fqdncacheUnlockEntry(fqdncache_entry * f) | |
597 | { | |
598 | if (f->locks == 0) { | |
599 | debug_trap("fqdncacheUnlockEntry: Entry has no locks"); | |
600 | return; | |
601 | } | |
602 | f->locks--; | |
603 | if (fqdncacheExpiredEntry(f)) | |
604 | fqdncache_release(f); | |
605 | } | |
606 | ||
ec878047 | 607 | static void |
608 | fqdncacheFreeEntry(void *data) | |
609 | { | |
610 | fqdncache_entry *f = data; | |
0e1a4726 | 611 | fqdn_pending *p = NULL; |
ec878047 | 612 | int k; |
0e1a4726 | 613 | while ((p = f->pending_head)) { |
614 | f->pending_head = p->next; | |
db1cd23c | 615 | memFree(p, MEM_FQDNCACHE_PENDING); |
0e1a4726 | 616 | } |
ec878047 | 617 | for (k = 0; k < (int) f->name_count; k++) |
618 | safe_free(f->names[k]); | |
619 | safe_free(f->name); | |
620 | safe_free(f->error_message); | |
db1cd23c | 621 | memFree(f, MEM_FQDNCACHE_ENTRY); |
ec878047 | 622 | } |
623 | ||
56e15c50 | 624 | void |
625 | fqdncacheFreeMemory(void) | |
626 | { | |
ec878047 | 627 | hashFreeItems(fqdn_table, fqdncacheFreeEntry); |
56e15c50 | 628 | hashFreeMemory(fqdn_table); |
afe95a7e | 629 | fqdn_table = NULL; |
56e15c50 | 630 | } |
429fdbec | 631 | |
632 | static void | |
633 | fqdncacheChangeKey(fqdncache_entry * f) | |
634 | { | |
635 | static int index = 0; | |
636 | LOCAL_ARRAY(char, new_key, 256); | |
637 | hash_link *table_entry = hash_lookup(fqdn_table, f->name); | |
638 | if (table_entry == NULL) { | |
6a78c18e | 639 | debug(35, 0) ("fqdncacheChangeKey: Could not find key '%s'\n", f->name); |
429fdbec | 640 | return; |
641 | } | |
642 | if (f != (fqdncache_entry *) table_entry) { | |
643 | debug_trap("fqdncacheChangeKey: f != table_entry!"); | |
644 | return; | |
645 | } | |
3fda0827 | 646 | hash_remove_link(fqdn_table, table_entry); |
042461c3 | 647 | snprintf(new_key, 256, "%d/", ++index); |
429fdbec | 648 | strncat(new_key, f->name, 128); |
6a78c18e | 649 | debug(35, 1) ("fqdncacheChangeKey: from '%s' to '%s'\n", f->name, new_key); |
429fdbec | 650 | safe_free(f->name); |
651 | f->name = xstrdup(new_key); | |
7db83ee8 | 652 | hash_join(fqdn_table, (hash_link *) f); |
429fdbec | 653 | } |
654 | ||
655 | /* call during reconfigure phase to clear out all the | |
656 | * pending and dispatched reqeusts that got lost */ | |
657 | void | |
658 | fqdncache_restart(void) | |
659 | { | |
660 | fqdncache_entry *this; | |
698e6f16 | 661 | assert(fqdn_table); |
0f6bebac | 662 | hash_first(fqdn_table); |
663 | while ((this = (fqdncache_entry *) hash_next(fqdn_table))) { | |
429fdbec | 664 | if (this->status == FQDN_CACHED) |
665 | continue; | |
666 | if (this->status == FQDN_NEGATIVE_CACHED) | |
667 | continue; | |
429fdbec | 668 | } |
e55650e3 | 669 | fqdncache_high = (long) (((float) Config.fqdncache.size * |
e144eae4 | 670 | (float) FQDN_HIGH_WATER) / (float) 100); |
e55650e3 | 671 | fqdncache_low = (long) (((float) Config.fqdncache.size * |
e144eae4 | 672 | (float) FQDN_LOW_WATER) / (float) 100); |
429fdbec | 673 | } |
ce75f381 | 674 | |
675 | #ifdef SQUID_SNMP | |
e7ef99a7 | 676 | /* |
135171fe | 677 | * The function to return the fqdn statistics via SNMP |
678 | */ | |
d60c11be | 679 | |
86115da5 | 680 | variable_list * |
e7ef99a7 | 681 | snmp_netFqdnFn(variable_list * Var, snint * ErrP) |
d60c11be | 682 | { |
86115da5 | 683 | variable_list *Answer; |
d60c11be | 684 | |
e7ef99a7 | 685 | debug(49, 5) ("snmp_netFqdnFn: Processing request:\n", Var->name[LEN_SQ_NET + |
135171fe | 686 | 1]); |
e7ef99a7 | 687 | snmpDebugOid(5, Var->name, Var->name_length); |
ce75f381 | 688 | |
86115da5 | 689 | Answer = snmp_var_new(Var->name, Var->name_length); |
690 | *ErrP = SNMP_ERR_NOERROR; | |
e7ef99a7 | 691 | Answer->val_len = sizeof(snint); |
2094fc54 | 692 | Answer->val.integer = memAllocate(MEM_SNMP_SNINT); |
e7ef99a7 | 693 | Answer->type = SMI_COUNTER32; |
694 | ||
135171fe | 695 | switch (Var->name[LEN_SQ_NET + 1]) { |
e7ef99a7 | 696 | case FQDN_ENT: |
135171fe | 697 | *(Answer->val.integer) = memInUse(MEM_FQDNCACHE_ENTRY); |
698 | Answer->type = SMI_GAUGE32; | |
699 | break; | |
e7ef99a7 | 700 | case FQDN_REQ: |
135171fe | 701 | *(Answer->val.integer) = FqdncacheStats.requests; |
702 | break; | |
e7ef99a7 | 703 | case FQDN_HITS: |
135171fe | 704 | *(Answer->val.integer) = FqdncacheStats.hits; |
705 | break; | |
e7ef99a7 | 706 | case FQDN_PENDHIT: |
135171fe | 707 | *(Answer->val.integer) = FqdncacheStats.pending_hits; |
708 | Answer->type = SMI_GAUGE32; | |
709 | break; | |
e7ef99a7 | 710 | case FQDN_NEGHIT: |
135171fe | 711 | *(Answer->val.integer) = FqdncacheStats.negative_hits; |
712 | break; | |
e7ef99a7 | 713 | case FQDN_MISS: |
135171fe | 714 | *(Answer->val.integer) = FqdncacheStats.misses; |
715 | break; | |
e7ef99a7 | 716 | case FQDN_GHBN: |
135171fe | 717 | *(Answer->val.integer) = FqdncacheStats.ghba_calls; |
718 | break; | |
ce75f381 | 719 | default: |
135171fe | 720 | *ErrP = SNMP_ERR_NOSUCHNAME; |
721 | snmp_var_free(Answer); | |
722 | return (NULL); | |
86115da5 | 723 | } |
724 | return Answer; | |
ce75f381 | 725 | } |
e7ef99a7 | 726 | |
135171fe | 727 | #endif /*SQUID_SNMP */ |