]>
Commit | Line | Data |
---|---|---|
f88bb09c | 1 | |
5999b776 | 2 | |
f88bb09c | 3 | /* |
2c4f7ab2 | 4 | * $Id: fqdncache.cc,v 1.118 1998/09/23 20:13:48 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 | struct fqdncacheQueueData { |
43 | struct fqdncacheQueueData *next; | |
44 | fqdncache_entry *f; | |
45 | }; | |
46 | ||
47 | static struct { | |
48 | int requests; | |
49 | int replies; | |
50 | int hits; | |
51 | int misses; | |
52 | int pending_hits; | |
53 | int negative_hits; | |
54 | int errors; | |
f88bb09c | 55 | int ghba_calls; /* # calls to blocking gethostbyaddr() */ |
56 | } FqdncacheStats; | |
57 | ||
4bc76d59 | 58 | static dlink_list lru_list; |
59 | ||
f5b8bbc4 | 60 | static void fqdncache_dnsHandleRead(int, void *); |
bd34f258 | 61 | static fqdncache_entry *fqdncacheParse(const char *buf, dnsserver_t *); |
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 *); | |
66 | static int fqdncacheHasPending(const fqdncache_entry *); | |
67 | static fqdncache_entry *fqdncache_get(const char *); | |
348b2031 | 68 | static FQDNH dummy_handler; |
f5b8bbc4 | 69 | static int fqdncacheExpiredEntry(const fqdncache_entry *); |
70 | static void fqdncacheAddPending(fqdncache_entry *, FQDNH *, void *); | |
71 | static void fqdncacheEnqueue(fqdncache_entry *); | |
72 | static void *fqdncacheDequeue(void); | |
73 | static void fqdncache_dnsDispatch(dnsserver_t *, fqdncache_entry *); | |
74 | static void fqdncacheChangeKey(fqdncache_entry * i); | |
75 | static void fqdncacheLockEntry(fqdncache_entry * f); | |
76 | static void fqdncacheUnlockEntry(fqdncache_entry * f); | |
ec878047 | 77 | static FREE fqdncacheFreeEntry; |
f88bb09c | 78 | |
365e5b34 | 79 | static hash_table *fqdn_table = NULL; |
f88bb09c | 80 | static struct fqdncacheQueueData *fqdncacheQueueHead = NULL; |
81 | static struct fqdncacheQueueData **fqdncacheQueueTailP = &fqdncacheQueueHead; | |
6a78c18e | 82 | static int queue_length = 0; |
f88bb09c | 83 | |
84 | static char fqdncache_status_char[] = | |
85 | { | |
86 | 'C', | |
87 | 'N', | |
88 | 'P', | |
89 | 'D' | |
90 | }; | |
91 | ||
24382924 | 92 | static long fqdncache_low = 180; |
93 | static long fqdncache_high = 200; | |
f88bb09c | 94 | |
b8d8561b | 95 | static void |
96 | fqdncacheEnqueue(fqdncache_entry * f) | |
f88bb09c | 97 | { |
6a78c18e | 98 | static time_t last_warning = 0; |
f88bb09c | 99 | struct fqdncacheQueueData *new = xcalloc(1, sizeof(struct fqdncacheQueueData)); |
100 | new->f = f; | |
101 | *fqdncacheQueueTailP = new; | |
102 | fqdncacheQueueTailP = &new->next; | |
6a78c18e | 103 | queue_length++; |
104 | if (queue_length < NDnsServersAlloc) | |
105 | return; | |
106 | if (squid_curtime - last_warning < 600) | |
107 | return; | |
108 | last_warning = squid_curtime; | |
109 | debug(35, 0) ("fqdncacheEnqueue: WARNING: All dnsservers are busy.\n"); | |
110 | debug(35, 0) ("fqdncacheEnqueue: WARNING: %d DNS lookups queued\n", queue_length); | |
111 | if (queue_length > NDnsServersAlloc * 2) | |
112 | fatal("Too many queued DNS lookups"); | |
113 | if (Config.dnsChildren >= DefaultDnsChildrenMax) | |
114 | return; | |
115 | debug(35, 1) ("fqdncacheEnqueue: Consider increasing 'dns_children' in your config file.\n"); | |
f88bb09c | 116 | } |
117 | ||
b8d8561b | 118 | static void * |
0673c0ba | 119 | fqdncacheDequeue(void) |
f88bb09c | 120 | { |
121 | struct fqdncacheQueueData *old = NULL; | |
122 | fqdncache_entry *f = NULL; | |
123 | if (fqdncacheQueueHead) { | |
124 | f = fqdncacheQueueHead->f; | |
125 | old = fqdncacheQueueHead; | |
126 | fqdncacheQueueHead = fqdncacheQueueHead->next; | |
127 | if (fqdncacheQueueHead == NULL) | |
128 | fqdncacheQueueTailP = &fqdncacheQueueHead; | |
129 | safe_free(old); | |
6a78c18e | 130 | queue_length--; |
f88bb09c | 131 | } |
429fdbec | 132 | if (f && f->status != FQDN_PENDING) |
133 | debug_trap("fqdncacheDequeue: status != FQDN_PENDING"); | |
f88bb09c | 134 | return f; |
135 | } | |
136 | ||
137 | /* removes the given fqdncache entry */ | |
b8d8561b | 138 | static void |
139 | fqdncache_release(fqdncache_entry * f) | |
f88bb09c | 140 | { |
f88bb09c | 141 | int k; |
e144eae4 | 142 | assert(f->status != FQDN_PENDING); |
143 | assert(f->status != FQDN_DISPATCHED); | |
144 | assert(f->pending_head == NULL); | |
3fda0827 | 145 | hash_remove_link(fqdn_table, (hash_link *) f); |
429fdbec | 146 | if (f->status == FQDN_CACHED) { |
f88bb09c | 147 | for (k = 0; k < (int) f->name_count; k++) |
148 | safe_free(f->names[k]); | |
a3d5953d | 149 | debug(35, 5) ("fqdncache_release: Released FQDN record for '%s'.\n", |
429fdbec | 150 | f->name); |
f88bb09c | 151 | } |
4bc76d59 | 152 | dlinkDelete(&f->lru, &lru_list); |
429fdbec | 153 | safe_free(f->name); |
154 | safe_free(f->error_message); | |
59c4d35b | 155 | memFree(MEM_FQDNCACHE_ENTRY, f); |
f88bb09c | 156 | } |
157 | ||
158 | /* return match for given name */ | |
b8d8561b | 159 | static fqdncache_entry * |
0ee4272b | 160 | fqdncache_get(const char *name) |
f88bb09c | 161 | { |
162 | hash_link *e; | |
163 | static fqdncache_entry *f; | |
164 | ||
165 | f = NULL; | |
166 | if (fqdn_table) { | |
167 | if ((e = hash_lookup(fqdn_table, name)) != NULL) | |
168 | f = (fqdncache_entry *) e; | |
169 | } | |
170 | return f; | |
171 | } | |
172 | ||
b8d8561b | 173 | static int |
fe4e214f | 174 | fqdncacheExpiredEntry(const fqdncache_entry * f) |
f88bb09c | 175 | { |
176 | if (f->status == FQDN_PENDING) | |
177 | return 0; | |
178 | if (f->status == FQDN_DISPATCHED) | |
179 | return 0; | |
429fdbec | 180 | if (f->locks != 0) |
181 | return 0; | |
e84703ad | 182 | if (f->expires > squid_curtime) |
f88bb09c | 183 | return 0; |
184 | return 1; | |
185 | } | |
186 | ||
59c4d35b | 187 | void |
188 | fqdncache_purgelru(void *notused) | |
f88bb09c | 189 | { |
4bc76d59 | 190 | dlink_node *m; |
191 | dlink_node *prev = NULL; | |
192 | fqdncache_entry *f; | |
f88bb09c | 193 | int removed = 0; |
52040193 | 194 | eventAdd("fqdncache_purgelru", fqdncache_purgelru, NULL, 10.0, 1); |
4bc76d59 | 195 | for (m = lru_list.tail; m; m = prev) { |
59c4d35b | 196 | if (memInUse(MEM_FQDNCACHE_ENTRY) < fqdncache_low) |
4bc76d59 | 197 | break; |
198 | prev = m->prev; | |
199 | f = m->data; | |
f88bb09c | 200 | if (f->status == FQDN_PENDING) |
201 | continue; | |
202 | if (f->status == FQDN_DISPATCHED) | |
203 | continue; | |
4bc76d59 | 204 | if (f->locks != 0) |
205 | continue; | |
206 | fqdncache_release(f); | |
f88bb09c | 207 | removed++; |
208 | } | |
6a78c18e | 209 | debug(35, 3) ("fqdncache_purgelru: removed %d entries\n", removed); |
f88bb09c | 210 | } |
211 | ||
212 | ||
213 | /* create blank fqdncache_entry */ | |
b8d8561b | 214 | static fqdncache_entry * |
429fdbec | 215 | fqdncache_create(const char *name) |
f88bb09c | 216 | { |
4bc76d59 | 217 | static fqdncache_entry *f; |
59c4d35b | 218 | f = memAllocate(MEM_FQDNCACHE_ENTRY); |
4bc76d59 | 219 | f->name = xstrdup(name); |
220 | f->expires = squid_curtime + Config.negativeDnsTtl; | |
7db83ee8 | 221 | hash_join(fqdn_table, (hash_link *) f); |
4bc76d59 | 222 | dlinkAdd(f, &f->lru, &lru_list); |
223 | return f; | |
f88bb09c | 224 | } |
225 | ||
b8d8561b | 226 | static void |
429fdbec | 227 | fqdncacheAddHostent(fqdncache_entry * f, const struct hostent *hp) |
f88bb09c | 228 | { |
229 | int k; | |
429fdbec | 230 | f->name_count = 0; |
231 | f->names[f->name_count++] = xstrdup((char *) hp->h_name); | |
232 | for (k = 0; hp->h_aliases[k]; k++) { | |
233 | f->names[f->name_count++] = xstrdup(hp->h_aliases[k]); | |
234 | if (f->name_count == FQDN_MAX_NAMES) | |
235 | break; | |
236 | } | |
237 | } | |
f88bb09c | 238 | |
429fdbec | 239 | static fqdncache_entry * |
240 | fqdncacheAddNew(const char *name, const struct hostent *hp, fqdncache_status_t status) | |
241 | { | |
242 | fqdncache_entry *f; | |
e144eae4 | 243 | assert(fqdncache_get(name) == NULL); |
6a78c18e | 244 | debug(35, 10) ("fqdncacheAddNew: Adding '%s', status=%c\n", |
429fdbec | 245 | name, |
246 | fqdncache_status_char[status]); | |
247 | f = fqdncache_create(name); | |
248 | if (hp) | |
249 | fqdncacheAddHostent(f, hp); | |
250 | f->status = status; | |
251 | f->lastref = squid_curtime; | |
252 | return f; | |
f88bb09c | 253 | } |
254 | ||
255 | /* walks down the pending list, calling handlers */ | |
b8d8561b | 256 | static void |
257 | fqdncache_call_pending(fqdncache_entry * f) | |
f88bb09c | 258 | { |
59c4d35b | 259 | fqdn_pending *p = NULL; |
f88bb09c | 260 | int nhandler = 0; |
261 | ||
262 | f->lastref = squid_curtime; | |
263 | ||
429fdbec | 264 | fqdncacheLockEntry(f); |
f88bb09c | 265 | while (f->pending_head != NULL) { |
266 | p = f->pending_head; | |
267 | f->pending_head = p->next; | |
268 | if (p->handler) { | |
269 | nhandler++; | |
270 | dns_error_message = f->error_message; | |
348b2031 | 271 | p->handler((f->status == FQDN_CACHED) ? f->names[0] : NULL, |
f88bb09c | 272 | p->handlerData); |
273 | } | |
59c4d35b | 274 | memFree(MEM_FQDNCACHE_PENDING, p); |
f88bb09c | 275 | } |
276 | f->pending_head = NULL; /* nuke list */ | |
a3d5953d | 277 | debug(35, 10) ("fqdncache_call_pending: Called %d handlers.\n", nhandler); |
429fdbec | 278 | fqdncacheUnlockEntry(f); |
f88bb09c | 279 | } |
280 | ||
bd34f258 | 281 | |
b8d8561b | 282 | static fqdncache_entry * |
bd34f258 | 283 | fqdncacheParse(const char *inbuf, dnsserver_t * dnsData) |
f88bb09c | 284 | { |
bd34f258 | 285 | LOCAL_ARRAY(char, buf, DNS_INBUF_SZ); |
e84703ad | 286 | char *token; |
287 | static fqdncache_entry f; | |
bd34f258 | 288 | int ttl; |
289 | xstrncpy(buf, inbuf, DNS_INBUF_SZ); | |
6a78c18e | 290 | debug(35, 5) ("fqdncacheParse: parsing:\n%s", buf); |
bd34f258 | 291 | memset(&f, '\0', sizeof(f)); |
292 | f.expires = squid_curtime; | |
293 | f.status = FQDN_NEGATIVE_CACHED; | |
294 | token = strtok(buf, w_space); | |
295 | if (NULL == token) { | |
6a78c18e | 296 | debug(35, 1) ("fqdncacheParse: Got <NULL>, expecting '$name'\n"); |
bd34f258 | 297 | return &f; |
298 | } | |
299 | if (0 == strcmp(token, "$fail")) { | |
300 | f.expires = squid_curtime + Config.negativeDnsTtl; | |
301 | token = strtok(NULL, "\n"); | |
302 | assert(NULL != token); | |
303 | f.error_message = xstrdup(token); | |
304 | return &f; | |
305 | } | |
306 | if (0 != strcmp(token, "$name")) { | |
6a78c18e | 307 | debug(35, 1) ("fqdncacheParse: Got '%s', expecting '$name'\n", token); |
bd34f258 | 308 | return &f; |
309 | } | |
310 | token = strtok(NULL, w_space); | |
311 | if (NULL == token) { | |
6a78c18e | 312 | debug(35, 1) ("fqdncacheParse: Got <NULL>, expecting TTL\n"); |
bd34f258 | 313 | return &f; |
314 | } | |
315 | f.status = FQDN_CACHED; | |
316 | ttl = atoi(token); | |
317 | if (ttl > 0) | |
318 | f.expires = squid_curtime + ttl; | |
319 | else | |
320 | f.expires = squid_curtime + Config.positiveDnsTtl; | |
321 | token = strtok(NULL, w_space); | |
322 | if (NULL != token) { | |
323 | f.names[0] = xstrdup(token); | |
324 | f.name_count = 1; | |
f88bb09c | 325 | } |
e84703ad | 326 | return &f; |
f88bb09c | 327 | } |
328 | ||
429fdbec | 329 | static void |
330 | fqdncacheNudgeQueue(void) | |
331 | { | |
332 | dnsserver_t *dnsData; | |
333 | fqdncache_entry *f = NULL; | |
334 | while ((dnsData = dnsGetFirstAvailable()) && (f = fqdncacheDequeue())) | |
335 | fqdncache_dnsDispatch(dnsData, f); | |
336 | } | |
337 | ||
b785f8ca | 338 | static void |
4f07153c | 339 | fqdncache_dnsHandleRead(int fd, void *data) |
f88bb09c | 340 | { |
2f549d83 | 341 | dnsserver_t *dnsData = data; |
f88bb09c | 342 | int len; |
f88bb09c | 343 | int n; |
344 | fqdncache_entry *f = NULL; | |
e84703ad | 345 | fqdncache_entry *x = NULL; |
f88bb09c | 346 | |
886f2785 | 347 | Counter.syscalls.sock.reads++; |
f88bb09c | 348 | len = read(fd, |
349 | dnsData->ip_inbuf + dnsData->offset, | |
350 | dnsData->size - dnsData->offset); | |
4f92c80c | 351 | fd_bytes(fd, len, FD_READ); |
a3d5953d | 352 | debug(35, 5) ("fqdncache_dnsHandleRead: Result from DNS ID %d (%d bytes)\n", |
f88bb09c | 353 | dnsData->id, len); |
354 | if (len <= 0) { | |
b224ea98 | 355 | if (len < 0 && ignoreErrno(errno)) { |
0a0bf5db | 356 | commSetSelect(fd, |
357 | COMM_SELECT_READ, | |
358 | fqdncache_dnsHandleRead, | |
359 | dnsData, 0); | |
360 | return; | |
361 | } | |
cd196bc8 | 362 | debug(35, dnsData->flags.closing ? 5 : 1) |
a3d5953d | 363 | ("FD %d: Connection from DNSSERVER #%d is closed, disabling\n", |
f88bb09c | 364 | fd, dnsData->id); |
cd196bc8 | 365 | dnsData->flags.alive = 0; |
366 | dnsData->flags.busy = 0; | |
367 | dnsData->flags.closing = 0; | |
368 | dnsData->flags.shutdown = 0; | |
b177367b | 369 | commSetSelect(fd, |
f88bb09c | 370 | COMM_SELECT_WRITE, |
371 | NULL, | |
b177367b | 372 | NULL, |
85d7ea98 | 373 | 0); |
f88bb09c | 374 | comm_close(fd); |
2f549d83 | 375 | return; |
f88bb09c | 376 | } |
377 | n = ++FqdncacheStats.replies; | |
378 | dnsData->offset += len; | |
379 | dnsData->ip_inbuf[dnsData->offset] = '\0'; | |
429fdbec | 380 | f = dnsData->data; |
698e6f16 | 381 | assert(f->status == FQDN_DISPATCHED); |
bd34f258 | 382 | if (strchr(dnsData->ip_inbuf, '\n')) { |
f88bb09c | 383 | /* end of record found */ |
fe522163 | 384 | DnsStats.replies++; |
12cf1be2 | 385 | statHistCount(&Counter.dns.svc_time, |
22f3fd98 | 386 | tvSubMsec(dnsData->dispatch_time, current_time)); |
bd34f258 | 387 | if ((x = fqdncacheParse(dnsData->ip_inbuf, dnsData)) == NULL) { |
388 | debug(35, 0) ("fqdncache_dnsHandleRead: fqdncacheParse failed?!\n"); | |
e84703ad | 389 | } else { |
390 | dnsData->offset = 0; | |
391 | dnsData->ip_inbuf[0] = '\0'; | |
e84703ad | 392 | f->name_count = x->name_count; |
7690e8eb | 393 | for (n = 0; n < (int) f->name_count; n++) |
e84703ad | 394 | f->names[n] = x->names[n]; |
395 | f->error_message = x->error_message; | |
396 | f->status = x->status; | |
397 | f->expires = x->expires; | |
398 | fqdncache_call_pending(f); | |
f88bb09c | 399 | } |
429fdbec | 400 | fqdncacheUnlockEntry(f); /* unlock from FQDN_DISPATCHED */ |
369e91e4 | 401 | } else { |
6a78c18e | 402 | debug(35, 5) ("fqdncache_dnsHandleRead: Incomplete reply\n"); |
067bea91 | 403 | commSetSelect(fd, |
404 | COMM_SELECT_READ, | |
405 | fqdncache_dnsHandleRead, | |
406 | dnsData, | |
407 | 0); | |
f88bb09c | 408 | } |
409 | if (dnsData->offset == 0) { | |
410 | dnsData->data = NULL; | |
cd196bc8 | 411 | dnsData->flags.busy = 0; |
412 | if (dnsData->flags.shutdown) | |
067bea91 | 413 | dnsShutdownServer(dnsData); |
414 | cbdataUnlock(dnsData); | |
f88bb09c | 415 | } |
429fdbec | 416 | fqdncacheNudgeQueue(); |
f88bb09c | 417 | } |
418 | ||
b8d8561b | 419 | static void |
348b2031 | 420 | fqdncacheAddPending(fqdncache_entry * f, FQDNH * handler, void *handlerData) |
f88bb09c | 421 | { |
59c4d35b | 422 | fqdn_pending *pending = memAllocate(MEM_FQDNCACHE_PENDING); |
423 | fqdn_pending **I = NULL; | |
429fdbec | 424 | f->lastref = squid_curtime; |
f88bb09c | 425 | pending->handler = handler; |
426 | pending->handlerData = handlerData; | |
f88bb09c | 427 | for (I = &(f->pending_head); *I; I = &((*I)->next)); |
428 | *I = pending; | |
4bc76d59 | 429 | if (f->status == FQDN_PENDING) |
429fdbec | 430 | fqdncacheNudgeQueue(); |
f88bb09c | 431 | } |
432 | ||
429fdbec | 433 | void |
348b2031 | 434 | fqdncache_nbgethostbyaddr(struct in_addr addr, FQDNH * handler, void *handlerData) |
f88bb09c | 435 | { |
436 | fqdncache_entry *f = NULL; | |
437 | dnsserver_t *dnsData = NULL; | |
438 | char *name = inet_ntoa(addr); | |
439 | ||
698e6f16 | 440 | assert(handler); |
f88bb09c | 441 | |
a3d5953d | 442 | debug(35, 4) ("fqdncache_nbgethostbyaddr: Name '%s'.\n", name); |
f88bb09c | 443 | FqdncacheStats.requests++; |
444 | ||
445 | if (name == NULL || name[0] == '\0') { | |
a3d5953d | 446 | debug(35, 4) ("fqdncache_nbgethostbyaddr: Invalid name!\n"); |
348b2031 | 447 | handler(NULL, handlerData); |
429fdbec | 448 | return; |
f88bb09c | 449 | } |
450 | if ((f = fqdncache_get(name))) { | |
451 | if (fqdncacheExpiredEntry(f)) { | |
452 | fqdncache_release(f); | |
453 | f = NULL; | |
454 | } | |
455 | } | |
456 | if (f == NULL) { | |
457 | /* MISS: No entry, create the new one */ | |
a3d5953d | 458 | debug(35, 5) ("fqdncache_nbgethostbyaddr: MISS for '%s'\n", name); |
f88bb09c | 459 | FqdncacheStats.misses++; |
429fdbec | 460 | f = fqdncacheAddNew(name, NULL, FQDN_PENDING); |
348b2031 | 461 | fqdncacheAddPending(f, handler, handlerData); |
f88bb09c | 462 | } else if (f->status == FQDN_CACHED || f->status == FQDN_NEGATIVE_CACHED) { |
463 | /* HIT */ | |
a3d5953d | 464 | debug(35, 4) ("fqdncache_nbgethostbyaddr: HIT for '%s'\n", name); |
f88bb09c | 465 | if (f->status == FQDN_NEGATIVE_CACHED) |
466 | FqdncacheStats.negative_hits++; | |
467 | else | |
468 | FqdncacheStats.hits++; | |
348b2031 | 469 | fqdncacheAddPending(f, handler, handlerData); |
f88bb09c | 470 | fqdncache_call_pending(f); |
429fdbec | 471 | return; |
f88bb09c | 472 | } else if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) { |
a3d5953d | 473 | debug(35, 4) ("fqdncache_nbgethostbyaddr: PENDING for '%s'\n", name); |
f88bb09c | 474 | FqdncacheStats.pending_hits++; |
348b2031 | 475 | fqdncacheAddPending(f, handler, handlerData); |
429fdbec | 476 | if (squid_curtime - f->expires > 600) { |
6a78c18e | 477 | debug(35, 0) ("fqdncache_nbgethostbyname: '%s' PENDING for %d seconds, aborting\n", name, |
ec878047 | 478 | (int) (squid_curtime + Config.negativeDnsTtl - f->expires)); |
429fdbec | 479 | fqdncacheChangeKey(f); |
480 | fqdncache_call_pending(f); | |
481 | } | |
482 | return; | |
f88bb09c | 483 | } else { |
d46a87a8 | 484 | debug(35, 1) ("fqdncache_nbgethostbyaddr: BAD status %d", |
485 | (int) f->status); | |
698e6f16 | 486 | assert(0); |
f88bb09c | 487 | } |
488 | ||
489 | /* for HIT, PENDING, DISPATCHED we've returned. For MISS we continue */ | |
490 | ||
429fdbec | 491 | if ((dnsData = dnsGetFirstAvailable())) { |
f88bb09c | 492 | fqdncache_dnsDispatch(dnsData, f); |
7c63efed | 493 | } else if (NDnsServersAlloc > 0) { |
f88bb09c | 494 | fqdncacheEnqueue(f); |
7c63efed | 495 | } else { |
496 | /* abort if we get here */ | |
497 | assert(NDnsServersAlloc); | |
429fdbec | 498 | } |
f88bb09c | 499 | } |
500 | ||
b8d8561b | 501 | static void |
502 | fqdncache_dnsDispatch(dnsserver_t * dns, fqdncache_entry * f) | |
f88bb09c | 503 | { |
504 | char *buf = NULL; | |
cd196bc8 | 505 | assert(dns->flags.alive); |
f88bb09c | 506 | if (!fqdncacheHasPending(f)) { |
a3d5953d | 507 | debug(35, 0) ("fqdncache_dnsDispatch: skipping '%s' because no handler.\n", |
f88bb09c | 508 | f->name); |
509 | f->status = FQDN_NEGATIVE_CACHED; | |
510 | fqdncache_release(f); | |
511 | return; | |
512 | } | |
429fdbec | 513 | if (f->status != FQDN_PENDING) |
514 | debug_trap("fqdncache_dnsDispatch: status != FQDN_PENDING"); | |
f88bb09c | 515 | buf = xcalloc(1, 256); |
2b9a078c | 516 | snprintf(buf, 256, "%s\n", f->name); |
cd196bc8 | 517 | dns->flags.busy = 1; |
f88bb09c | 518 | dns->data = f; |
429fdbec | 519 | f->status = FQDN_DISPATCHED; |
f88bb09c | 520 | comm_write(dns->outpipe, |
521 | buf, | |
522 | strlen(buf), | |
f88bb09c | 523 | NULL, /* Handler */ |
524 | NULL, /* Handler-data */ | |
525 | xfree); | |
369e91e4 | 526 | cbdataLock(dns); |
b177367b | 527 | commSetSelect(dns->outpipe, |
f88bb09c | 528 | COMM_SELECT_READ, |
2f549d83 | 529 | fqdncache_dnsHandleRead, |
b177367b | 530 | dns, |
531 | 0); | |
a3d5953d | 532 | debug(35, 5) ("fqdncache_dnsDispatch: Request sent to DNS server #%d.\n", |
f88bb09c | 533 | dns->id); |
534 | dns->dispatch_time = current_time; | |
535 | DnsStats.requests++; | |
536 | DnsStats.hist[dns->id - 1]++; | |
4bc76d59 | 537 | fqdncacheLockEntry(f); /* lock while FQDN_DISPATCHED */ |
f88bb09c | 538 | } |
539 | ||
540 | ||
541 | /* initialize the fqdncache */ | |
b8d8561b | 542 | void |
0673c0ba | 543 | fqdncache_init(void) |
f88bb09c | 544 | { |
aa9e2cab | 545 | int n; |
19054954 | 546 | if (fqdn_table) |
547 | return; | |
a3d5953d | 548 | debug(35, 3) ("Initializing FQDN Cache...\n"); |
f88bb09c | 549 | memset(&FqdncacheStats, '\0', sizeof(FqdncacheStats)); |
3eb55834 | 550 | memset(&lru_list, '\0', sizeof(lru_list)); |
e55650e3 | 551 | fqdncache_high = (long) (((float) Config.fqdncache.size * |
f88bb09c | 552 | (float) FQDN_HIGH_WATER) / (float) 100); |
e55650e3 | 553 | fqdncache_low = (long) (((float) Config.fqdncache.size * |
f88bb09c | 554 | (float) FQDN_LOW_WATER) / (float) 100); |
aa9e2cab | 555 | n = hashPrime(fqdncache_high / 4); |
6a78c18e | 556 | fqdn_table = hash_create((HASHCMP *) strcmp, n, hash4); |
22f3fd98 | 557 | cachemgrRegister("fqdncache", |
558 | "FQDN Cache Stats and Contents", | |
1da3b90b | 559 | fqdnStats, 0, 1); |
f88bb09c | 560 | } |
561 | ||
562 | /* clean up the pending entries in dnsserver */ | |
563 | /* return 1 if we found the host, 0 otherwise */ | |
b8d8561b | 564 | int |
b69f7771 | 565 | fqdncacheUnregister(struct in_addr addr, void *data) |
f88bb09c | 566 | { |
03047798 | 567 | char *name = inet_ntoa(addr); |
f88bb09c | 568 | fqdncache_entry *f = NULL; |
59c4d35b | 569 | fqdn_pending *p = NULL; |
f88bb09c | 570 | int n = 0; |
5a675d84 | 571 | debug(35, 3) ("fqdncacheUnregister: name '%s'\n", name); |
f88bb09c | 572 | if ((f = fqdncache_get(name)) == NULL) |
573 | return 0; | |
574 | if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) { | |
575 | for (p = f->pending_head; p; p = p->next) { | |
b69f7771 | 576 | if (p->handlerData != data) |
577 | continue; | |
578 | p->handler = NULL; | |
b69f7771 | 579 | n++; |
f88bb09c | 580 | } |
581 | } | |
b69f7771 | 582 | if (n == 0) |
583 | debug_trap("fqdncacheUnregister: callback data not found"); | |
a3d5953d | 584 | debug(35, 3) ("fqdncacheUnregister: unregistered %d handlers\n", n); |
f88bb09c | 585 | return n; |
586 | } | |
587 | ||
0ee4272b | 588 | const char * |
b8d8561b | 589 | fqdncache_gethostbyaddr(struct in_addr addr, int flags) |
f88bb09c | 590 | { |
591 | char *name = inet_ntoa(addr); | |
592 | fqdncache_entry *f = NULL; | |
429fdbec | 593 | struct in_addr ip; |
698e6f16 | 594 | assert(name); |
f88bb09c | 595 | FqdncacheStats.requests++; |
596 | if ((f = fqdncache_get(name))) { | |
429fdbec | 597 | if (fqdncacheExpiredEntry(f)) { |
598 | fqdncache_release(f); | |
599 | f = NULL; | |
600 | } | |
601 | } | |
602 | if (f) { | |
7c63efed | 603 | if (f->status == FQDN_NEGATIVE_CACHED) { |
f88bb09c | 604 | FqdncacheStats.negative_hits++; |
605 | dns_error_message = f->error_message; | |
606 | return NULL; | |
607 | } else { | |
608 | FqdncacheStats.hits++; | |
609 | f->lastref = squid_curtime; | |
610 | return f->names[0]; | |
611 | } | |
612 | } | |
f88bb09c | 613 | /* check if it's already a FQDN address in text form. */ |
429fdbec | 614 | if (!safe_inet_addr(name, &ip)) |
f88bb09c | 615 | return name; |
429fdbec | 616 | FqdncacheStats.misses++; |
f88bb09c | 617 | if (flags & FQDN_LOOKUP_IF_MISS) |
348b2031 | 618 | fqdncache_nbgethostbyaddr(addr, dummy_handler, NULL); |
f88bb09c | 619 | return NULL; |
620 | } | |
621 | ||
622 | ||
623 | /* process objects list */ | |
b8d8561b | 624 | void |
625 | fqdnStats(StoreEntry * sentry) | |
f88bb09c | 626 | { |
627 | fqdncache_entry *f = NULL; | |
628 | int k; | |
629 | int ttl; | |
4bc76d59 | 630 | if (fqdn_table == NULL) |
f88bb09c | 631 | return; |
15576b6a | 632 | storeAppendPrintf(sentry, "FQDN Cache Statistics:\n"); |
633 | storeAppendPrintf(sentry, "FQDNcache Entries: %d\n", | |
59c4d35b | 634 | memInUse(MEM_FQDNCACHE_ENTRY)); |
15576b6a | 635 | storeAppendPrintf(sentry, "FQDNcache Requests: %d\n", |
f88bb09c | 636 | FqdncacheStats.requests); |
15576b6a | 637 | storeAppendPrintf(sentry, "FQDNcache Hits: %d\n", |
f88bb09c | 638 | FqdncacheStats.hits); |
15576b6a | 639 | storeAppendPrintf(sentry, "FQDNcache Pending Hits: %d\n", |
f88bb09c | 640 | FqdncacheStats.pending_hits); |
15576b6a | 641 | storeAppendPrintf(sentry, "FQDNcache Negative Hits: %d\n", |
f88bb09c | 642 | FqdncacheStats.negative_hits); |
15576b6a | 643 | storeAppendPrintf(sentry, "FQDNcache Misses: %d\n", |
f88bb09c | 644 | FqdncacheStats.misses); |
15576b6a | 645 | storeAppendPrintf(sentry, "Blocking calls to gethostbyaddr(): %d\n", |
f88bb09c | 646 | FqdncacheStats.ghba_calls); |
6a78c18e | 647 | storeAppendPrintf(sentry, "pending queue length: %d\n", queue_length); |
15576b6a | 648 | storeAppendPrintf(sentry, "FQDN Cache Contents:\n\n"); |
f88bb09c | 649 | |
0f6bebac | 650 | hash_first(fqdn_table); |
651 | while ((f = (fqdncache_entry *) hash_next(fqdn_table))) { | |
f88bb09c | 652 | if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) |
653 | ttl = 0; | |
654 | else | |
e84703ad | 655 | ttl = (f->expires - squid_curtime); |
15576b6a | 656 | storeAppendPrintf(sentry, " %-32.32s %c %6d %d", |
f88bb09c | 657 | f->name, |
658 | fqdncache_status_char[f->status], | |
659 | ttl, | |
660 | (int) f->name_count); | |
661 | for (k = 0; k < (int) f->name_count; k++) | |
662 | storeAppendPrintf(sentry, " %s", f->names[k]); | |
22f3fd98 | 663 | storeAppendPrintf(sentry, "\n"); |
f88bb09c | 664 | } |
f88bb09c | 665 | } |
666 | ||
b8d8561b | 667 | static void |
79d39a72 | 668 | dummy_handler(const char *bufnotused, void *datanotused) |
f88bb09c | 669 | { |
670 | return; | |
671 | } | |
672 | ||
b8d8561b | 673 | static int |
fe4e214f | 674 | fqdncacheHasPending(const fqdncache_entry * f) |
f88bb09c | 675 | { |
59c4d35b | 676 | const fqdn_pending *p = NULL; |
f88bb09c | 677 | if (f->status != FQDN_PENDING) |
678 | return 0; | |
679 | for (p = f->pending_head; p; p = p->next) | |
680 | if (p->handler) | |
681 | return 1; | |
682 | return 0; | |
683 | } | |
684 | ||
b8d8561b | 685 | void |
0ee4272b | 686 | fqdncacheReleaseInvalid(const char *name) |
f88bb09c | 687 | { |
688 | fqdncache_entry *f; | |
689 | if ((f = fqdncache_get(name)) == NULL) | |
690 | return; | |
691 | if (f->status != FQDN_NEGATIVE_CACHED) | |
692 | return; | |
693 | fqdncache_release(f); | |
694 | } | |
28ab0c0a | 695 | |
0ee4272b | 696 | const char * |
b8d8561b | 697 | fqdnFromAddr(struct in_addr addr) |
28ab0c0a | 698 | { |
0ee4272b | 699 | const char *n; |
39de381a | 700 | static char buf[32]; |
17a0a4ee | 701 | if (Config.onoff.log_fqdn && (n = fqdncache_gethostbyaddr(addr, 0))) |
28ab0c0a | 702 | return n; |
f2052513 | 703 | xstrncpy(buf, inet_ntoa(addr), 32); |
39de381a | 704 | return buf; |
28ab0c0a | 705 | } |
f201f309 | 706 | |
429fdbec | 707 | static void |
708 | fqdncacheLockEntry(fqdncache_entry * f) | |
709 | { | |
4bc76d59 | 710 | if (f->locks++ == 0) { |
711 | dlinkDelete(&f->lru, &lru_list); | |
712 | dlinkAdd(f, &f->lru, &lru_list); | |
713 | } | |
429fdbec | 714 | } |
715 | ||
716 | static void | |
717 | fqdncacheUnlockEntry(fqdncache_entry * f) | |
718 | { | |
719 | if (f->locks == 0) { | |
720 | debug_trap("fqdncacheUnlockEntry: Entry has no locks"); | |
721 | return; | |
722 | } | |
723 | f->locks--; | |
724 | if (fqdncacheExpiredEntry(f)) | |
725 | fqdncache_release(f); | |
726 | } | |
727 | ||
ec878047 | 728 | static void |
729 | fqdncacheFreeEntry(void *data) | |
730 | { | |
731 | fqdncache_entry *f = data; | |
0e1a4726 | 732 | fqdn_pending *p = NULL; |
ec878047 | 733 | int k; |
0e1a4726 | 734 | while ((p = f->pending_head)) { |
735 | f->pending_head = p->next; | |
736 | memFree(MEM_FQDNCACHE_PENDING, p); | |
737 | } | |
ec878047 | 738 | for (k = 0; k < (int) f->name_count; k++) |
739 | safe_free(f->names[k]); | |
740 | safe_free(f->name); | |
741 | safe_free(f->error_message); | |
742 | memFree(MEM_FQDNCACHE_ENTRY, f); | |
743 | } | |
744 | ||
56e15c50 | 745 | void |
746 | fqdncacheFreeMemory(void) | |
747 | { | |
ec878047 | 748 | hashFreeItems(fqdn_table, fqdncacheFreeEntry); |
56e15c50 | 749 | hashFreeMemory(fqdn_table); |
afe95a7e | 750 | fqdn_table = NULL; |
56e15c50 | 751 | } |
429fdbec | 752 | |
753 | static void | |
754 | fqdncacheChangeKey(fqdncache_entry * f) | |
755 | { | |
756 | static int index = 0; | |
757 | LOCAL_ARRAY(char, new_key, 256); | |
758 | hash_link *table_entry = hash_lookup(fqdn_table, f->name); | |
759 | if (table_entry == NULL) { | |
6a78c18e | 760 | debug(35, 0) ("fqdncacheChangeKey: Could not find key '%s'\n", f->name); |
429fdbec | 761 | return; |
762 | } | |
763 | if (f != (fqdncache_entry *) table_entry) { | |
764 | debug_trap("fqdncacheChangeKey: f != table_entry!"); | |
765 | return; | |
766 | } | |
3fda0827 | 767 | hash_remove_link(fqdn_table, table_entry); |
042461c3 | 768 | snprintf(new_key, 256, "%d/", ++index); |
429fdbec | 769 | strncat(new_key, f->name, 128); |
6a78c18e | 770 | debug(35, 1) ("fqdncacheChangeKey: from '%s' to '%s'\n", f->name, new_key); |
429fdbec | 771 | safe_free(f->name); |
772 | f->name = xstrdup(new_key); | |
7db83ee8 | 773 | hash_join(fqdn_table, (hash_link *) f); |
429fdbec | 774 | } |
775 | ||
776 | /* call during reconfigure phase to clear out all the | |
777 | * pending and dispatched reqeusts that got lost */ | |
778 | void | |
779 | fqdncache_restart(void) | |
780 | { | |
781 | fqdncache_entry *this; | |
698e6f16 | 782 | assert(fqdn_table); |
429fdbec | 783 | while (fqdncacheDequeue()); |
0f6bebac | 784 | hash_first(fqdn_table); |
785 | while ((this = (fqdncache_entry *) hash_next(fqdn_table))) { | |
429fdbec | 786 | if (this->status == FQDN_CACHED) |
787 | continue; | |
788 | if (this->status == FQDN_NEGATIVE_CACHED) | |
789 | continue; | |
429fdbec | 790 | } |
e55650e3 | 791 | fqdncache_high = (long) (((float) Config.fqdncache.size * |
e144eae4 | 792 | (float) FQDN_HIGH_WATER) / (float) 100); |
e55650e3 | 793 | fqdncache_low = (long) (((float) Config.fqdncache.size * |
e144eae4 | 794 | (float) FQDN_LOW_WATER) / (float) 100); |
429fdbec | 795 | } |
ce75f381 | 796 | |
797 | #ifdef SQUID_SNMP | |
ce75f381 | 798 | |
d60c11be | 799 | int |
800 | fqdn_getMax() | |
801 | { | |
86115da5 | 802 | int i = 0; |
803 | fqdncache_entry *fq = NULL; | |
d60c11be | 804 | |
0f6bebac | 805 | hash_first(fqdn_table); |
806 | while ((fq = (fqdncache_entry *) hash_next(fqdn_table))) | |
807 | i++; | |
86115da5 | 808 | return i; |
809 | } | |
d60c11be | 810 | |
86115da5 | 811 | variable_list * |
ec878047 | 812 | snmp_fqdncacheFn(variable_list * Var, snint * ErrP) |
d60c11be | 813 | { |
86115da5 | 814 | variable_list *Answer; |
815 | static fqdncache_entry *fq = NULL; | |
816 | static struct in_addr fqaddr; | |
817 | int cnt = 0; | |
d60c11be | 818 | |
86115da5 | 819 | debug(49, 5) ("snmp_fqdncacheFn: Processing request with %d.%d!\n", Var->name[11], Var->name[12]); |
ce75f381 | 820 | |
86115da5 | 821 | cnt = Var->name[12]; |
d60c11be | 822 | |
0f6bebac | 823 | hash_first(fqdn_table); |
824 | while (cnt--) { | |
d60c11be | 825 | fq = (fqdncache_entry *) hash_next(fqdn_table); |
0f6bebac | 826 | if (NULL == fq) |
eeb423fb | 827 | break; |
0f6bebac | 828 | } |
2c4f7ab2 | 829 | hash_last(fqdn_table); |
86115da5 | 830 | if (fq == NULL || cnt != 0) { |
d60c11be | 831 | *ErrP = SNMP_ERR_NOSUCHNAME; |
832 | return NULL; | |
86115da5 | 833 | } |
834 | Answer = snmp_var_new(Var->name, Var->name_length); | |
835 | *ErrP = SNMP_ERR_NOERROR; | |
d60c11be | 836 | |
86115da5 | 837 | switch (Var->name[11]) { |
ce75f381 | 838 | case NET_FQDN_ID: |
d60c11be | 839 | Answer->type = ASN_INTEGER; |
c6da280c | 840 | Answer->val_len = sizeof(snint); |
451b07c5 | 841 | Answer->val.integer = xmalloc(Answer->val_len); |
86115da5 | 842 | *(Answer->val.integer) = Var->name[12]; |
d60c11be | 843 | break; |
ce75f381 | 844 | case NET_FQDN_NAME: |
86115da5 | 845 | Answer->type = SMI_STRING; |
846 | Answer->val_len = strlen(fq->names[0]); | |
399e85ea | 847 | Answer->val.string = (u_char *) xstrdup(fq->names[0]); |
d60c11be | 848 | break; |
ce75f381 | 849 | case NET_FQDN_IP: |
d60c11be | 850 | Answer->type = SMI_IPADDRESS; |
c6da280c | 851 | Answer->val_len = sizeof(snint); |
451b07c5 | 852 | Answer->val.integer = xmalloc(Answer->val_len); |
86115da5 | 853 | safe_inet_addr(fq->name, &fqaddr); |
c6da280c | 854 | *(Answer->val.integer) = (snint) fqaddr.s_addr; |
d60c11be | 855 | break; |
ce75f381 | 856 | case NET_FQDN_LASTREF: |
d60c11be | 857 | Answer->type = SMI_TIMETICKS; |
c6da280c | 858 | Answer->val_len = sizeof(snint); |
451b07c5 | 859 | Answer->val.integer = xmalloc(Answer->val_len); |
2ac76861 | 860 | *(Answer->val.integer) = squid_curtime - fq->lastref; |
d60c11be | 861 | break; |
ce75f381 | 862 | case NET_FQDN_EXPIRES: |
d60c11be | 863 | Answer->type = SMI_TIMETICKS; |
c6da280c | 864 | Answer->val_len = sizeof(snint); |
451b07c5 | 865 | Answer->val.integer = xmalloc(Answer->val_len); |
2ac76861 | 866 | *(Answer->val.integer) = fq->expires - squid_curtime; |
d60c11be | 867 | break; |
ce75f381 | 868 | case NET_FQDN_STATE: |
d60c11be | 869 | Answer->type = ASN_INTEGER; |
c6da280c | 870 | Answer->val_len = sizeof(snint); |
451b07c5 | 871 | Answer->val.integer = xmalloc(Answer->val_len); |
86115da5 | 872 | *(Answer->val.integer) = fq->status; |
d60c11be | 873 | break; |
ce75f381 | 874 | default: |
86115da5 | 875 | *ErrP = SNMP_ERR_NOSUCHNAME; |
876 | snmp_var_free(Answer); | |
86115da5 | 877 | return (NULL); |
878 | } | |
879 | return Answer; | |
ce75f381 | 880 | } |
881 | #endif |