]> git.ipfire.org Git - thirdparty/squid.git/blame - src/fqdncache.cc
Minor modifications, clean up the static situation
[thirdparty/squid.git] / src / fqdncache.cc
CommitLineData
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 42struct fqdncacheQueueData {
43 struct fqdncacheQueueData *next;
44 fqdncache_entry *f;
45};
46
47static 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 58static dlink_list lru_list;
59
f5b8bbc4 60static void fqdncache_dnsHandleRead(int, void *);
bd34f258 61static fqdncache_entry *fqdncacheParse(const char *buf, dnsserver_t *);
f5b8bbc4 62static void fqdncache_release(fqdncache_entry *);
f5b8bbc4 63static fqdncache_entry *fqdncache_create(const char *name);
f5b8bbc4 64static void fqdncache_call_pending(fqdncache_entry *);
65static void fqdncacheAddHostent(fqdncache_entry *, const struct hostent *);
66static int fqdncacheHasPending(const fqdncache_entry *);
67static fqdncache_entry *fqdncache_get(const char *);
348b2031 68static FQDNH dummy_handler;
f5b8bbc4 69static int fqdncacheExpiredEntry(const fqdncache_entry *);
70static void fqdncacheAddPending(fqdncache_entry *, FQDNH *, void *);
71static void fqdncacheEnqueue(fqdncache_entry *);
72static void *fqdncacheDequeue(void);
73static void fqdncache_dnsDispatch(dnsserver_t *, fqdncache_entry *);
74static void fqdncacheChangeKey(fqdncache_entry * i);
75static void fqdncacheLockEntry(fqdncache_entry * f);
76static void fqdncacheUnlockEntry(fqdncache_entry * f);
ec878047 77static FREE fqdncacheFreeEntry;
f88bb09c 78
365e5b34 79static hash_table *fqdn_table = NULL;
f88bb09c 80static struct fqdncacheQueueData *fqdncacheQueueHead = NULL;
81static struct fqdncacheQueueData **fqdncacheQueueTailP = &fqdncacheQueueHead;
6a78c18e 82static int queue_length = 0;
f88bb09c 83
84static char fqdncache_status_char[] =
85{
86 'C',
87 'N',
88 'P',
89 'D'
90};
91
24382924 92static long fqdncache_low = 180;
93static long fqdncache_high = 200;
f88bb09c 94
b8d8561b 95static void
96fqdncacheEnqueue(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 118static void *
0673c0ba 119fqdncacheDequeue(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 138static void
139fqdncache_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 159static fqdncache_entry *
0ee4272b 160fqdncache_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 173static int
fe4e214f 174fqdncacheExpiredEntry(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 187void
188fqdncache_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 214static fqdncache_entry *
429fdbec 215fqdncache_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 226static void
429fdbec 227fqdncacheAddHostent(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 239static fqdncache_entry *
240fqdncacheAddNew(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 256static void
257fqdncache_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 282static fqdncache_entry *
bd34f258 283fqdncacheParse(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 329static void
330fqdncacheNudgeQueue(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 338static void
4f07153c 339fqdncache_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 419static void
348b2031 420fqdncacheAddPending(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 433void
348b2031 434fqdncache_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 501static void
502fqdncache_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 542void
0673c0ba 543fqdncache_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 564int
b69f7771 565fqdncacheUnregister(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 588const char *
b8d8561b 589fqdncache_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 624void
625fqdnStats(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 667static void
79d39a72 668dummy_handler(const char *bufnotused, void *datanotused)
f88bb09c 669{
670 return;
671}
672
b8d8561b 673static int
fe4e214f 674fqdncacheHasPending(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 685void
0ee4272b 686fqdncacheReleaseInvalid(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 696const char *
b8d8561b 697fqdnFromAddr(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 707static void
708fqdncacheLockEntry(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
716static void
717fqdncacheUnlockEntry(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 728static void
729fqdncacheFreeEntry(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 745void
746fqdncacheFreeMemory(void)
747{
ec878047 748 hashFreeItems(fqdn_table, fqdncacheFreeEntry);
56e15c50 749 hashFreeMemory(fqdn_table);
afe95a7e 750 fqdn_table = NULL;
56e15c50 751}
429fdbec 752
753static void
754fqdncacheChangeKey(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 */
778void
779fqdncache_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 799int
800fqdn_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 811variable_list *
ec878047 812snmp_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