]> git.ipfire.org Git - thirdparty/squid.git/blame - src/fqdncache.cc
- Change some sizes to 'size_t'
[thirdparty/squid.git] / src / fqdncache.cc
CommitLineData
f88bb09c 1
2/*
365e5b34 3 * $Id: fqdncache.cc,v 1.57 1997/06/26 22:35:46 wessels Exp $
f88bb09c 4 *
7cf620a9 5 * DEBUG: section 35 FQDN Cache
f88bb09c 6 * AUTHOR: Harvest Derived
7 *
42c04c16 8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
f88bb09c 9 * --------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by
14 * the National Science Foundation.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 *
30 */
31
32/*
33 * Copyright (c) 1994, 1995. All rights reserved.
34 *
35 * The Harvest software was developed by the Internet Research Task
36 * Force Research Group on Resource Discovery (IRTF-RD):
37 *
38 * Mic Bowman of Transarc Corporation.
39 * Peter Danzig of the University of Southern California.
40 * Darren R. Hardy of the University of Colorado at Boulder.
41 * Udi Manber of the University of Arizona.
42 * Michael F. Schwartz of the University of Colorado at Boulder.
43 * Duane Wessels of the University of Colorado at Boulder.
44 *
45 * This copyright notice applies to software in the Harvest
46 * ``src/'' directory only. Users should consult the individual
47 * copyright notices in the ``components/'' subdirectories for
48 * copyright information about other software bundled with the
49 * Harvest source code distribution.
50 *
51 * TERMS OF USE
52 *
53 * The Harvest software may be used and re-distributed without
54 * charge, provided that the software origin and research team are
55 * cited in any use of the system. Most commonly this is
56 * accomplished by including a link to the Harvest Home Page
57 * (http://harvest.cs.colorado.edu/) from the query page of any
58 * Broker you deploy, as well as in the query result pages. These
59 * links are generated automatically by the standard Broker
60 * software distribution.
61 *
62 * The Harvest software is provided ``as is'', without express or
63 * implied warranty, and with no support nor obligation to assist
64 * in its use, correction, modification or enhancement. We assume
65 * no liability with respect to the infringement of copyrights,
66 * trade secrets, or any patents, and are not responsible for
67 * consequential damages. Proper use of the Harvest software is
68 * entirely the responsibility of the user.
69 *
70 * DERIVATIVE WORKS
71 *
72 * Users may make derivative works from the Harvest software, subject
73 * to the following constraints:
74 *
75 * - You must include the above copyright notice and these
76 * accompanying paragraphs in all forms of derivative works,
77 * and any documentation and other materials related to such
78 * distribution and use acknowledge that the software was
79 * developed at the above institutions.
80 *
81 * - You must notify IRTF-RD regarding your distribution of
82 * the derivative work.
83 *
84 * - You must clearly notify users that your are distributing
85 * a modified version and not the original Harvest software.
86 *
87 * - Any derivative product is also subject to these copyright
88 * and use restrictions.
89 *
90 * Note that the Harvest software is NOT in the public domain. We
91 * retain copyright, as specified above.
92 *
93 * HISTORY OF FREE SOFTWARE STATUS
94 *
95 * Originally we required sites to license the software in cases
96 * where they were going to build commercial products/services
97 * around Harvest. In June 1995 we changed this policy. We now
98 * allow people to use the core Harvest software (the code found in
99 * the Harvest ``src/'' directory) for free. We made this change
100 * in the interest of encouraging the widest possible deployment of
101 * the technology. The Harvest software is really a reference
102 * implementation of a set of protocols and formats, some of which
103 * we intend to standardize. We encourage commercial
104 * re-implementations of code complying to this set of standards.
105 */
106
107#include "squid.h"
108
109#define MAX_LINELEN (4096)
110
111#define MAX_FQDN 1024 /* Maximum cached FQDN */
112#define FQDN_LOW_WATER 90
113#define FQDN_HIGH_WATER 95
f88bb09c 114
115struct _fqdn_pending {
582b6456 116 FQDNH *handler;
f88bb09c 117 void *handlerData;
118 struct _fqdn_pending *next;
119};
120
121struct fqdncacheQueueData {
122 struct fqdncacheQueueData *next;
123 fqdncache_entry *f;
124};
125
126static struct {
127 int requests;
128 int replies;
129 int hits;
130 int misses;
131 int pending_hits;
132 int negative_hits;
133 int errors;
134 int avg_svc_time;
135 int ghba_calls; /* # calls to blocking gethostbyaddr() */
136} FqdncacheStats;
137
67508012 138static int fqdncache_compareLastRef _PARAMS((fqdncache_entry **, fqdncache_entry **));
00046e88 139static void fqdncache_dnsHandleRead _PARAMS((int, void *));
0ee4272b 140static fqdncache_entry *fqdncache_parsebuffer _PARAMS((const char *buf, dnsserver_t *));
67508012 141static int fqdncache_purgelru _PARAMS((void));
142static void fqdncache_release _PARAMS((fqdncache_entry *));
143static fqdncache_entry *fqdncache_GetFirst _PARAMS((void));
144static fqdncache_entry *fqdncache_GetNext _PARAMS((void));
429fdbec 145static fqdncache_entry *fqdncache_create _PARAMS((const char *name));
67508012 146static void fqdncache_add_to_hash _PARAMS((fqdncache_entry *));
147static void fqdncache_call_pending _PARAMS((fqdncache_entry *));
429fdbec 148static void fqdncacheAddHostent _PARAMS((fqdncache_entry *, const struct hostent *));
0ee4272b 149static int fqdncacheHasPending _PARAMS((const fqdncache_entry *));
150static fqdncache_entry *fqdncache_get _PARAMS((const char *));
348b2031 151static FQDNH dummy_handler;
0ee4272b 152static int fqdncacheExpiredEntry _PARAMS((const fqdncache_entry *));
348b2031 153static void fqdncacheAddPending _PARAMS((fqdncache_entry *, FQDNH *, void *));
67508012 154static void fqdncacheEnqueue _PARAMS((fqdncache_entry *));
155static void *fqdncacheDequeue _PARAMS((void));
156static void fqdncache_dnsDispatch _PARAMS((dnsserver_t *, fqdncache_entry *));
429fdbec 157static void fqdncacheChangeKey _PARAMS((fqdncache_entry * i));
158static void fqdncacheLockEntry _PARAMS((fqdncache_entry * f));
159static void fqdncacheUnlockEntry _PARAMS((fqdncache_entry * f));
f88bb09c 160
365e5b34 161static hash_table *fqdn_table = NULL;
f88bb09c 162static struct fqdncacheQueueData *fqdncacheQueueHead = NULL;
163static struct fqdncacheQueueData **fqdncacheQueueTailP = &fqdncacheQueueHead;
164
165static char fqdncache_status_char[] =
166{
167 'C',
168 'N',
169 'P',
170 'D'
171};
172
24382924 173static long fqdncache_low = 180;
174static long fqdncache_high = 200;
f88bb09c 175
b8d8561b 176static void
177fqdncacheEnqueue(fqdncache_entry * f)
f88bb09c 178{
179 struct fqdncacheQueueData *new = xcalloc(1, sizeof(struct fqdncacheQueueData));
180 new->f = f;
181 *fqdncacheQueueTailP = new;
182 fqdncacheQueueTailP = &new->next;
183}
184
b8d8561b 185static void *
0673c0ba 186fqdncacheDequeue(void)
f88bb09c 187{
188 struct fqdncacheQueueData *old = NULL;
189 fqdncache_entry *f = NULL;
190 if (fqdncacheQueueHead) {
191 f = fqdncacheQueueHead->f;
192 old = fqdncacheQueueHead;
193 fqdncacheQueueHead = fqdncacheQueueHead->next;
194 if (fqdncacheQueueHead == NULL)
195 fqdncacheQueueTailP = &fqdncacheQueueHead;
196 safe_free(old);
197 }
429fdbec 198 if (f && f->status != FQDN_PENDING)
199 debug_trap("fqdncacheDequeue: status != FQDN_PENDING");
f88bb09c 200 return f;
201}
202
203/* removes the given fqdncache entry */
b8d8561b 204static void
205fqdncache_release(fqdncache_entry * f)
f88bb09c 206{
f88bb09c 207 hash_link *table_entry = NULL;
208 int k;
209
210 if ((table_entry = hash_lookup(fqdn_table, f->name)) == NULL) {
a3d5953d 211 debug(35, 0) ("fqdncache_release: Could not find key '%s'\n", f->name);
f88bb09c 212 return;
213 }
429fdbec 214 if (f != (fqdncache_entry *) table_entry)
215 fatal_dump("fqdncache_release: f != table_entry!");
f88bb09c 216 if (f->status == FQDN_PENDING) {
a3d5953d 217 debug(35, 1) ("fqdncache_release: Someone called on a PENDING entry\n");
f88bb09c 218 return;
219 }
220 if (f->status == FQDN_DISPATCHED) {
a3d5953d 221 debug(35, 1) ("fqdncache_release: Someone called on a DISPATCHED entry\n");
f88bb09c 222 return;
223 }
348b2031 224 if (f->pending_head)
225 fatal_dump("fqdncache_release: still have pending clients");
f88bb09c 226 if (hash_remove_link(fqdn_table, table_entry)) {
a3d5953d 227 debug(35, 0) ("fqdncache_release: hash_remove_link() failed for '%s'\n",
429fdbec 228 f->name);
f88bb09c 229 return;
230 }
429fdbec 231 if (f->status == FQDN_CACHED) {
f88bb09c 232 for (k = 0; k < (int) f->name_count; k++)
233 safe_free(f->names[k]);
a3d5953d 234 debug(35, 5) ("fqdncache_release: Released FQDN record for '%s'.\n",
429fdbec 235 f->name);
f88bb09c 236 }
429fdbec 237 safe_free(f->name);
238 safe_free(f->error_message);
239 safe_free(f);
f88bb09c 240 --meta_data.fqdncache_count;
241 return;
242}
243
244/* return match for given name */
b8d8561b 245static fqdncache_entry *
0ee4272b 246fqdncache_get(const char *name)
f88bb09c 247{
248 hash_link *e;
249 static fqdncache_entry *f;
250
251 f = NULL;
252 if (fqdn_table) {
253 if ((e = hash_lookup(fqdn_table, name)) != NULL)
254 f = (fqdncache_entry *) e;
255 }
256 return f;
257}
258
b8d8561b 259static fqdncache_entry *
0673c0ba 260fqdncache_GetFirst(void)
f88bb09c 261{
262 return (fqdncache_entry *) hash_first(fqdn_table);
263}
264
b8d8561b 265static fqdncache_entry *
0673c0ba 266fqdncache_GetNext(void)
f88bb09c 267{
268 return (fqdncache_entry *) hash_next(fqdn_table);
269}
270
b8d8561b 271static int
272fqdncache_compareLastRef(fqdncache_entry ** e1, fqdncache_entry ** e2)
f88bb09c 273{
274 if (!e1 || !e2)
275 fatal_dump(NULL);
276 if ((*e1)->lastref > (*e2)->lastref)
277 return (1);
278 if ((*e1)->lastref < (*e2)->lastref)
279 return (-1);
280 return (0);
281}
282
b8d8561b 283static int
fe4e214f 284fqdncacheExpiredEntry(const fqdncache_entry * f)
f88bb09c 285{
286 if (f->status == FQDN_PENDING)
287 return 0;
288 if (f->status == FQDN_DISPATCHED)
289 return 0;
429fdbec 290 if (f->locks != 0)
291 return 0;
e84703ad 292 if (f->expires > squid_curtime)
f88bb09c 293 return 0;
294 return 1;
295}
296
297/* finds the LRU and deletes */
b8d8561b 298static int
0673c0ba 299fqdncache_purgelru(void)
f88bb09c 300{
301 fqdncache_entry *f = NULL;
302 int local_fqdn_count = 0;
303 int local_fqdn_notpending_count = 0;
304 int removed = 0;
305 int k;
306 fqdncache_entry **LRU_list = NULL;
307 int LRU_list_count = 0;
308 int LRU_cur_size = meta_data.fqdncache_count;
309
310 LRU_list = xcalloc(LRU_cur_size, sizeof(fqdncache_entry *));
311
312 for (f = fqdncache_GetFirst(); f; f = fqdncache_GetNext()) {
313 if (fqdncacheExpiredEntry(f)) {
314 fqdncache_release(f);
315 removed++;
316 continue;
317 }
318 local_fqdn_count++;
319
320 if (LRU_list_count >= LRU_cur_size) {
321 /* have to realloc */
322 LRU_cur_size += 16;
a3d5953d 323 debug(35, 3) ("fqdncache_purgelru: Have to grow LRU_list to %d. This shouldn't happen.\n",
f88bb09c 324 LRU_cur_size);
325 LRU_list = xrealloc((char *) LRU_list,
326 LRU_cur_size * sizeof(fqdncache_entry *));
327 }
328 if (f->status == FQDN_PENDING)
329 continue;
330 if (f->status == FQDN_DISPATCHED)
331 continue;
332 local_fqdn_notpending_count++;
333 LRU_list[LRU_list_count++] = f;
334 }
335
a3d5953d 336 debug(35, 3) ("fqdncache_purgelru: fqdncache_count: %5d\n", meta_data.fqdncache_count);
337 debug(35, 3) (" actual count : %5d\n", local_fqdn_count);
338 debug(35, 3) (" high W mark : %5d\n", fqdncache_high);
339 debug(35, 3) (" low W mark : %5d\n", fqdncache_low);
340 debug(35, 3) (" not pending : %5d\n", local_fqdn_notpending_count);
341 debug(35, 3) (" LRU candidates : %5d\n", LRU_list_count);
f88bb09c 342
343 /* sort LRU candidate list */
344 qsort((char *) LRU_list,
345 LRU_list_count,
429fdbec 346 sizeof(fqdncache_entry *),
582b6456 347 (QS *) fqdncache_compareLastRef);
429fdbec 348 for (k = 0; k < LRU_list_count; k++) {
349 if (meta_data.fqdncache_count < fqdncache_low)
350 break;
351 if (LRU_list[k] == NULL)
352 break;
f88bb09c 353 fqdncache_release(LRU_list[k]);
354 removed++;
355 }
356
a3d5953d 357 debug(35, 3) (" removed : %5d\n", removed);
f88bb09c 358 safe_free(LRU_list);
359 return (removed > 0) ? 0 : -1;
360}
361
362
363/* create blank fqdncache_entry */
b8d8561b 364static fqdncache_entry *
429fdbec 365fqdncache_create(const char *name)
f88bb09c 366{
367 static fqdncache_entry *new;
368
369 if (meta_data.fqdncache_count > fqdncache_high) {
370 if (fqdncache_purgelru() < 0)
a3d5953d 371 debug(35, 0) ("HELP!! FQDN Cache is overflowing!\n");
f88bb09c 372 }
373 meta_data.fqdncache_count++;
374 new = xcalloc(1, sizeof(fqdncache_entry));
429fdbec 375 new->name = xstrdup(name);
376 new->expires = squid_curtime + Config.negativeDnsTtl;
377 fqdncache_add_to_hash(new);
f88bb09c 378 return new;
f88bb09c 379}
380
b8d8561b 381static void
382fqdncache_add_to_hash(fqdncache_entry * f)
f88bb09c 383{
384 if (hash_join(fqdn_table, (hash_link *) f)) {
a3d5953d 385 debug(35, 1) ("fqdncache_add_to_hash: Cannot add %s (%p) to hash table %d.\n",
f88bb09c 386 f->name, f, fqdn_table);
387 }
a3d5953d 388 debug(35, 5) ("fqdncache_add_to_hash: name <%s>\n", f->name);
f88bb09c 389}
390
b8d8561b 391static void
429fdbec 392fqdncacheAddHostent(fqdncache_entry * f, const struct hostent *hp)
f88bb09c 393{
394 int k;
429fdbec 395 f->name_count = 0;
396 f->names[f->name_count++] = xstrdup((char *) hp->h_name);
397 for (k = 0; hp->h_aliases[k]; k++) {
398 f->names[f->name_count++] = xstrdup(hp->h_aliases[k]);
399 if (f->name_count == FQDN_MAX_NAMES)
400 break;
401 }
402}
f88bb09c 403
429fdbec 404static fqdncache_entry *
405fqdncacheAddNew(const char *name, const struct hostent *hp, fqdncache_status_t status)
406{
407 fqdncache_entry *f;
f88bb09c 408 if (fqdncache_get(name))
429fdbec 409 fatal_dump("fqdncacheAddNew: somebody adding a duplicate!");
a3d5953d 410 debug(14, 10) ("fqdncacheAddNew: Adding '%s', status=%c\n",
429fdbec 411 name,
412 fqdncache_status_char[status]);
413 f = fqdncache_create(name);
414 if (hp)
415 fqdncacheAddHostent(f, hp);
416 f->status = status;
417 f->lastref = squid_curtime;
418 return f;
f88bb09c 419}
420
421/* walks down the pending list, calling handlers */
b8d8561b 422static void
423fqdncache_call_pending(fqdncache_entry * f)
f88bb09c 424{
425 struct _fqdn_pending *p = NULL;
426 int nhandler = 0;
427
428 f->lastref = squid_curtime;
429
429fdbec 430 fqdncacheLockEntry(f);
f88bb09c 431 while (f->pending_head != NULL) {
432 p = f->pending_head;
433 f->pending_head = p->next;
434 if (p->handler) {
435 nhandler++;
436 dns_error_message = f->error_message;
348b2031 437 p->handler((f->status == FQDN_CACHED) ? f->names[0] : NULL,
f88bb09c 438 p->handlerData);
439 }
f88bb09c 440 safe_free(p);
441 }
442 f->pending_head = NULL; /* nuke list */
a3d5953d 443 debug(35, 10) ("fqdncache_call_pending: Called %d handlers.\n", nhandler);
429fdbec 444 fqdncacheUnlockEntry(f);
f88bb09c 445}
446
b8d8561b 447static fqdncache_entry *
fe4e214f 448fqdncache_parsebuffer(const char *inbuf, dnsserver_t * dnsData)
f88bb09c 449{
e84703ad 450 char *buf = xstrdup(inbuf);
451 char *token;
452 static fqdncache_entry f;
453 int k;
f88bb09c 454 int ipcount;
455 int aliascount;
a3d5953d 456 debug(35, 5) ("fqdncache_parsebuffer: parsing:\n%s", inbuf);
e84703ad 457 memset(&f, '\0', sizeof(fqdncache_entry));
458 f.expires = squid_curtime + Config.positiveDnsTtl;
620da955 459 f.status = FQDN_DISPATCHED;
e84703ad 460 for (token = strtok(buf, w_space); token; token = strtok(NULL, w_space)) {
461 if (!strcmp(token, "$end")) {
f88bb09c 462 break;
e84703ad 463 } else if (!strcmp(token, "$alive")) {
f88bb09c 464 dnsData->answer = squid_curtime;
e84703ad 465 } else if (!strcmp(token, "$fail")) {
4bcfeca1 466 if ((token = strtok(NULL, "\n")) == NULL)
e84703ad 467 fatal_dump("Invalid $fail");
468 f.expires = squid_curtime + Config.negativeDnsTtl;
620da955 469 f.status = FQDN_NEGATIVE_CACHED;
e84703ad 470 } else if (!strcmp(token, "$message")) {
471 if ((token = strtok(NULL, "\n")) == NULL)
472 fatal_dump("Invalid $message");
473 f.error_message = xstrdup(token);
474 } else if (!strcmp(token, "$name")) {
475 if ((token = strtok(NULL, w_space)) == NULL)
476 fatal_dump("Invalid $name");
620da955 477 f.status = FQDN_CACHED;
e84703ad 478 } else if (!strcmp(token, "$h_name")) {
479 if ((token = strtok(NULL, w_space)) == NULL)
480 fatal_dump("Invalid $h_name");
481 f.names[0] = xstrdup(token);
482 f.name_count = 1;
483 } else if (!strcmp(token, "$h_len")) {
484 if ((token = strtok(NULL, w_space)) == NULL)
485 fatal_dump("Invalid $h_len");
486 } else if (!strcmp(token, "$ipcount")) {
487 if ((token = strtok(NULL, w_space)) == NULL)
488 fatal_dump("Invalid $ipcount");
489 ipcount = atoi(token);
490 for (k = 0; k < ipcount; k++) {
491 if ((token = strtok(NULL, w_space)) == NULL)
620da955 492 fatal_dump("Invalid FQDN address");
f88bb09c 493 }
e84703ad 494 } else if (!strcmp(token, "$aliascount")) {
495 if ((token = strtok(NULL, w_space)) == NULL)
496 fatal_dump("Invalid $aliascount");
497 aliascount = atoi(token);
498 for (k = 0; k < aliascount; k++) {
499 if ((token = strtok(NULL, w_space)) == NULL)
500 fatal_dump("Invalid alias");
f88bb09c 501 }
e84703ad 502 } else if (!strcmp(token, "$ttl")) {
503 if ((token = strtok(NULL, w_space)) == NULL)
504 fatal_dump("Invalid $ttl");
505 f.expires = squid_curtime + atoi(token);
f88bb09c 506 } else {
e84703ad 507 fatal_dump("Invalid dnsserver output");
f88bb09c 508 }
509 }
e84703ad 510 xfree(buf);
511 return &f;
f88bb09c 512}
513
429fdbec 514static void
515fqdncacheNudgeQueue(void)
516{
517 dnsserver_t *dnsData;
518 fqdncache_entry *f = NULL;
519 while ((dnsData = dnsGetFirstAvailable()) && (f = fqdncacheDequeue()))
520 fqdncache_dnsDispatch(dnsData, f);
521}
522
b785f8ca 523static void
4f07153c 524fqdncache_dnsHandleRead(int fd, void *data)
f88bb09c 525{
2f549d83 526 dnsserver_t *dnsData = data;
f88bb09c 527 int len;
f88bb09c 528 int n;
529 fqdncache_entry *f = NULL;
e84703ad 530 fqdncache_entry *x = NULL;
f88bb09c 531
532 len = read(fd,
533 dnsData->ip_inbuf + dnsData->offset,
534 dnsData->size - dnsData->offset);
4f92c80c 535 fd_bytes(fd, len, FD_READ);
a3d5953d 536 debug(35, 5) ("fqdncache_dnsHandleRead: Result from DNS ID %d (%d bytes)\n",
f88bb09c 537 dnsData->id, len);
538 if (len <= 0) {
0a0bf5db 539 if (len < 0 && (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)) {
540 commSetSelect(fd,
541 COMM_SELECT_READ,
542 fqdncache_dnsHandleRead,
543 dnsData, 0);
544 return;
545 }
a3d5953d 546 debug(35, dnsData->flags & DNS_FLAG_CLOSING ? 5 : 1)
547 ("FD %d: Connection from DNSSERVER #%d is closed, disabling\n",
f88bb09c 548 fd, dnsData->id);
549 dnsData->flags = 0;
b177367b 550 commSetSelect(fd,
f88bb09c 551 COMM_SELECT_WRITE,
552 NULL,
b177367b 553 NULL,
85d7ea98 554 0);
f88bb09c 555 comm_close(fd);
2f549d83 556 return;
f88bb09c 557 }
558 n = ++FqdncacheStats.replies;
b0c9fba9 559 DnsStats.replies++;
f88bb09c 560 dnsData->offset += len;
561 dnsData->ip_inbuf[dnsData->offset] = '\0';
429fdbec 562 f = dnsData->data;
563 if (f->status != FQDN_DISPATCHED)
564 fatal_dump("fqdncache_dnsHandleRead: bad status");
f88bb09c 565 if (strstr(dnsData->ip_inbuf, "$end\n")) {
566 /* end of record found */
9e4ad609 567 FqdncacheStats.avg_svc_time =
568 intAverage(FqdncacheStats.avg_svc_time,
569 tvSubMsec(dnsData->dispatch_time, current_time),
570 n, FQDNCACHE_AV_FACTOR);
e84703ad 571 if ((x = fqdncache_parsebuffer(dnsData->ip_inbuf, dnsData)) == NULL) {
a3d5953d 572 debug(35, 0) ("fqdncache_dnsHandleRead: fqdncache_parsebuffer failed?!\n");
e84703ad 573 } else {
574 dnsData->offset = 0;
575 dnsData->ip_inbuf[0] = '\0';
e84703ad 576 f->name_count = x->name_count;
7690e8eb 577 for (n = 0; n < (int) f->name_count; n++)
e84703ad 578 f->names[n] = x->names[n];
579 f->error_message = x->error_message;
580 f->status = x->status;
581 f->expires = x->expires;
582 fqdncache_call_pending(f);
f88bb09c 583 }
429fdbec 584 fqdncacheUnlockEntry(f); /* unlock from FQDN_DISPATCHED */
f88bb09c 585 }
586 if (dnsData->offset == 0) {
587 dnsData->data = NULL;
588 dnsData->flags &= ~DNS_FLAG_BUSY;
589 }
2f549d83 590 /* reschedule */
591 commSetSelect(dnsData->inpipe,
592 COMM_SELECT_READ,
593 fqdncache_dnsHandleRead,
594 dnsData, 0);
429fdbec 595 fqdncacheNudgeQueue();
f88bb09c 596}
597
b8d8561b 598static void
348b2031 599fqdncacheAddPending(fqdncache_entry * f, FQDNH * handler, void *handlerData)
f88bb09c 600{
601 struct _fqdn_pending *pending = xcalloc(1, sizeof(struct _fqdn_pending));
602 struct _fqdn_pending **I = NULL;
429fdbec 603 f->lastref = squid_curtime;
f88bb09c 604 pending->handler = handler;
605 pending->handlerData = handlerData;
f88bb09c 606 for (I = &(f->pending_head); *I; I = &((*I)->next));
607 *I = pending;
429fdbec 608 if (f->status == IP_PENDING)
609 fqdncacheNudgeQueue();
f88bb09c 610}
611
429fdbec 612void
348b2031 613fqdncache_nbgethostbyaddr(struct in_addr addr, FQDNH * handler, void *handlerData)
f88bb09c 614{
615 fqdncache_entry *f = NULL;
616 dnsserver_t *dnsData = NULL;
617 char *name = inet_ntoa(addr);
618
619 if (!handler)
620 fatal_dump("fqdncache_nbgethostbyaddr: NULL handler");
621
a3d5953d 622 debug(35, 4) ("fqdncache_nbgethostbyaddr: Name '%s'.\n", name);
f88bb09c 623 FqdncacheStats.requests++;
624
625 if (name == NULL || name[0] == '\0') {
a3d5953d 626 debug(35, 4) ("fqdncache_nbgethostbyaddr: Invalid name!\n");
348b2031 627 handler(NULL, handlerData);
429fdbec 628 return;
f88bb09c 629 }
630 if ((f = fqdncache_get(name))) {
631 if (fqdncacheExpiredEntry(f)) {
632 fqdncache_release(f);
633 f = NULL;
634 }
635 }
636 if (f == NULL) {
637 /* MISS: No entry, create the new one */
a3d5953d 638 debug(35, 5) ("fqdncache_nbgethostbyaddr: MISS for '%s'\n", name);
f88bb09c 639 FqdncacheStats.misses++;
429fdbec 640 f = fqdncacheAddNew(name, NULL, FQDN_PENDING);
348b2031 641 fqdncacheAddPending(f, handler, handlerData);
f88bb09c 642 } else if (f->status == FQDN_CACHED || f->status == FQDN_NEGATIVE_CACHED) {
643 /* HIT */
a3d5953d 644 debug(35, 4) ("fqdncache_nbgethostbyaddr: HIT for '%s'\n", name);
f88bb09c 645 if (f->status == FQDN_NEGATIVE_CACHED)
646 FqdncacheStats.negative_hits++;
647 else
648 FqdncacheStats.hits++;
348b2031 649 fqdncacheAddPending(f, handler, handlerData);
f88bb09c 650 fqdncache_call_pending(f);
429fdbec 651 return;
f88bb09c 652 } else if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) {
a3d5953d 653 debug(35, 4) ("fqdncache_nbgethostbyaddr: PENDING for '%s'\n", name);
f88bb09c 654 FqdncacheStats.pending_hits++;
348b2031 655 fqdncacheAddPending(f, handler, handlerData);
429fdbec 656 if (squid_curtime - f->expires > 600) {
a3d5953d 657 debug(14, 0) ("fqdncache_nbgethostbyname: '%s' PENDING for %d seconds, aborting\n", name, squid_curtime + Config.negativeDnsTtl - f->expires);
429fdbec 658 fqdncacheChangeKey(f);
659 fqdncache_call_pending(f);
660 }
661 return;
f88bb09c 662 } else {
663 fatal_dump("fqdncache_nbgethostbyaddr: BAD fqdncache_entry status");
664 }
665
666 /* for HIT, PENDING, DISPATCHED we've returned. For MISS we continue */
667
429fdbec 668 if ((dnsData = dnsGetFirstAvailable())) {
f88bb09c 669 fqdncache_dnsDispatch(dnsData, f);
429fdbec 670 return;
671 }
672 if (NDnsServersAlloc > 0) {
f88bb09c 673 fqdncacheEnqueue(f);
429fdbec 674 return;
675 }
676 fqdncache_gethostbyaddr(addr, FQDN_BLOCKING_LOOKUP);
677 fqdncache_call_pending(f);
f88bb09c 678}
679
b8d8561b 680static void
681fqdncache_dnsDispatch(dnsserver_t * dns, fqdncache_entry * f)
f88bb09c 682{
683 char *buf = NULL;
9d169279 684 if (!BIT_TEST(dns->flags, DNS_FLAG_ALIVE))
0c77d853 685 debug_trap("Dispatching a dead DNS server");
f88bb09c 686 if (!fqdncacheHasPending(f)) {
a3d5953d 687 debug(35, 0) ("fqdncache_dnsDispatch: skipping '%s' because no handler.\n",
f88bb09c 688 f->name);
689 f->status = FQDN_NEGATIVE_CACHED;
690 fqdncache_release(f);
691 return;
692 }
429fdbec 693 if (f->status != FQDN_PENDING)
694 debug_trap("fqdncache_dnsDispatch: status != FQDN_PENDING");
f88bb09c 695 buf = xcalloc(1, 256);
696 sprintf(buf, "%1.254s\n", f->name);
697 dns->flags |= DNS_FLAG_BUSY;
698 dns->data = f;
429fdbec 699 f->status = FQDN_DISPATCHED;
f88bb09c 700 comm_write(dns->outpipe,
701 buf,
702 strlen(buf),
f88bb09c 703 NULL, /* Handler */
704 NULL, /* Handler-data */
705 xfree);
b177367b 706 commSetSelect(dns->outpipe,
f88bb09c 707 COMM_SELECT_READ,
2f549d83 708 fqdncache_dnsHandleRead,
b177367b 709 dns,
710 0);
a3d5953d 711 debug(35, 5) ("fqdncache_dnsDispatch: Request sent to DNS server #%d.\n",
f88bb09c 712 dns->id);
713 dns->dispatch_time = current_time;
714 DnsStats.requests++;
715 DnsStats.hist[dns->id - 1]++;
429fdbec 716 fqdncacheLockEntry(f); /* lock while IP_DISPATCHED */
f88bb09c 717}
718
719
720/* initialize the fqdncache */
b8d8561b 721void
0673c0ba 722fqdncache_init(void)
f88bb09c 723{
19054954 724 if (fqdn_table)
725 return;
a3d5953d 726 debug(35, 3) ("Initializing FQDN Cache...\n");
f88bb09c 727 memset(&FqdncacheStats, '\0', sizeof(FqdncacheStats));
aabcd584 728 /* small hash table */
1b50df4b 729 fqdn_table = hash_create(urlcmp, 229, hash4);
f88bb09c 730 fqdncache_high = (long) (((float) MAX_FQDN *
731 (float) FQDN_HIGH_WATER) / (float) 100);
732 fqdncache_low = (long) (((float) MAX_FQDN *
733 (float) FQDN_LOW_WATER) / (float) 100);
734}
735
736/* clean up the pending entries in dnsserver */
737/* return 1 if we found the host, 0 otherwise */
b8d8561b 738int
b69f7771 739fqdncacheUnregister(struct in_addr addr, void *data)
f88bb09c 740{
03047798 741 char *name = inet_ntoa(addr);
f88bb09c 742 fqdncache_entry *f = NULL;
743 struct _fqdn_pending *p = NULL;
744 int n = 0;
a3d5953d 745 debug(35, 3) ("fqdncacheUnregister: FD %d, name '%s'\n", name);
f88bb09c 746 if ((f = fqdncache_get(name)) == NULL)
747 return 0;
748 if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) {
749 for (p = f->pending_head; p; p = p->next) {
b69f7771 750 if (p->handlerData != data)
751 continue;
752 p->handler = NULL;
b69f7771 753 n++;
f88bb09c 754 }
755 }
b69f7771 756 if (n == 0)
757 debug_trap("fqdncacheUnregister: callback data not found");
a3d5953d 758 debug(35, 3) ("fqdncacheUnregister: unregistered %d handlers\n", n);
f88bb09c 759 return n;
760}
761
0ee4272b 762const char *
b8d8561b 763fqdncache_gethostbyaddr(struct in_addr addr, int flags)
f88bb09c 764{
765 char *name = inet_ntoa(addr);
766 fqdncache_entry *f = NULL;
0ee4272b 767 const struct hostent *hp = NULL;
429fdbec 768 struct in_addr ip;
e924600d 769 static char *static_name = NULL;
f88bb09c 770
771 if (!name)
772 fatal_dump("fqdncache_gethostbyaddr: NULL name");
773 FqdncacheStats.requests++;
774 if ((f = fqdncache_get(name))) {
429fdbec 775 if (fqdncacheExpiredEntry(f)) {
776 fqdncache_release(f);
777 f = NULL;
778 }
779 }
780 if (f) {
f88bb09c 781 if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) {
429fdbec 782 if (!BIT_TEST(flags, IP_BLOCKING_LOOKUP)) {
783 FqdncacheStats.pending_hits++;
784 return NULL;
785 }
f88bb09c 786 } else if (f->status == FQDN_NEGATIVE_CACHED) {
787 FqdncacheStats.negative_hits++;
788 dns_error_message = f->error_message;
789 return NULL;
790 } else {
791 FqdncacheStats.hits++;
792 f->lastref = squid_curtime;
793 return f->names[0];
794 }
795 }
f88bb09c 796 /* check if it's already a FQDN address in text form. */
429fdbec 797 if (!safe_inet_addr(name, &ip))
f88bb09c 798 return name;
429fdbec 799 FqdncacheStats.misses++;
800 if (BIT_TEST(flags, FQDN_BLOCKING_LOOKUP)) {
801 if (NDnsServersAlloc)
a3d5953d 802 debug(14, 0) ("WARNING: blocking on gethostbyaddr() for '%s'\n", name);
f88bb09c 803 FqdncacheStats.ghba_calls++;
429fdbec 804 hp = gethostbyaddr((char *) &ip.s_addr, 4, AF_INET);
f88bb09c 805 if (hp && hp->h_name && (hp->h_name[0] != '\0') && fqdn_table) {
429fdbec 806 if (f == NULL) {
807 f = fqdncacheAddNew(name, hp, FQDN_CACHED);
808 } else if (f->status == FQDN_DISPATCHED) {
809 /* only dnsHandleRead() can change from DISPATCHED to CACHED */
e924600d 810 xfree(static_name);
811 static_name = xstrdup(hp->h_name);
812 return static_name;
429fdbec 813 } else {
814 fqdncacheAddHostent(f, hp);
815 f->status = FQDN_CACHED;
e924600d 816 }
429fdbec 817 f->expires = squid_curtime + Config.positiveDnsTtl;
f88bb09c 818 return f->names[0];
819 }
820 /* bad address, negative cached */
429fdbec 821 if (fqdn_table && f == NULL) {
822 f = fqdncacheAddNew(name, hp, FQDN_NEGATIVE_CACHED);
823 f->expires = squid_curtime + Config.negativeDnsTtl;
824 return NULL;
825 }
f88bb09c 826 }
827 if (flags & FQDN_LOOKUP_IF_MISS)
348b2031 828 fqdncache_nbgethostbyaddr(addr, dummy_handler, NULL);
f88bb09c 829 return NULL;
830}
831
832
833/* process objects list */
b8d8561b 834void
835fqdnStats(StoreEntry * sentry)
f88bb09c 836{
837 fqdncache_entry *f = NULL;
838 int k;
839 int ttl;
840
841 if (!fqdn_table)
842 return;
843
844 storeAppendPrintf(sentry, "{FQDN Cache Statistics:\n");
845 storeAppendPrintf(sentry, "{FQDNcache Entries: %d}\n",
846 meta_data.fqdncache_count);
847 storeAppendPrintf(sentry, "{FQDNcache Requests: %d}\n",
848 FqdncacheStats.requests);
849 storeAppendPrintf(sentry, "{FQDNcache Hits: %d}\n",
850 FqdncacheStats.hits);
851 storeAppendPrintf(sentry, "{FQDNcache Pending Hits: %d}\n",
852 FqdncacheStats.pending_hits);
853 storeAppendPrintf(sentry, "{FQDNcache Negative Hits: %d}\n",
854 FqdncacheStats.negative_hits);
855 storeAppendPrintf(sentry, "{FQDNcache Misses: %d}\n",
856 FqdncacheStats.misses);
857 storeAppendPrintf(sentry, "{Blocking calls to gethostbyaddr(): %d}\n",
858 FqdncacheStats.ghba_calls);
859 storeAppendPrintf(sentry, "{dnsserver avg service time: %d msec}\n",
860 FqdncacheStats.avg_svc_time);
861 storeAppendPrintf(sentry, "}\n\n");
862 storeAppendPrintf(sentry, "{FQDN Cache Contents:\n\n");
863
864 for (f = fqdncache_GetFirst(); f; f = fqdncache_GetNext()) {
865 if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED)
866 ttl = 0;
867 else
e84703ad 868 ttl = (f->expires - squid_curtime);
f88bb09c 869 storeAppendPrintf(sentry, " {%-32.32s %c %6d %d",
870 f->name,
871 fqdncache_status_char[f->status],
872 ttl,
873 (int) f->name_count);
874 for (k = 0; k < (int) f->name_count; k++)
875 storeAppendPrintf(sentry, " %s", f->names[k]);
876 storeAppendPrintf(sentry, close_bracket);
877 }
878 storeAppendPrintf(sentry, close_bracket);
879}
880
b8d8561b 881static void
348b2031 882dummy_handler(const char *u2, void *u3)
f88bb09c 883{
884 return;
885}
886
b8d8561b 887static int
fe4e214f 888fqdncacheHasPending(const fqdncache_entry * f)
f88bb09c 889{
0ee4272b 890 const struct _fqdn_pending *p = NULL;
f88bb09c 891 if (f->status != FQDN_PENDING)
892 return 0;
893 for (p = f->pending_head; p; p = p->next)
894 if (p->handler)
895 return 1;
896 return 0;
897}
898
b8d8561b 899void
0ee4272b 900fqdncacheReleaseInvalid(const char *name)
f88bb09c 901{
902 fqdncache_entry *f;
903 if ((f = fqdncache_get(name)) == NULL)
904 return;
905 if (f->status != FQDN_NEGATIVE_CACHED)
906 return;
907 fqdncache_release(f);
908}
28ab0c0a 909
0ee4272b 910const char *
b8d8561b 911fqdnFromAddr(struct in_addr addr)
28ab0c0a 912{
0ee4272b 913 const char *n;
39de381a 914 static char buf[32];
b0c9fba9 915 if (Config.Log.log_fqdn && (n = fqdncache_gethostbyaddr(addr, 0)))
28ab0c0a 916 return n;
f2052513 917 xstrncpy(buf, inet_ntoa(addr), 32);
39de381a 918 return buf;
28ab0c0a 919}
f201f309 920
b8d8561b 921int
0673c0ba 922fqdncacheQueueDrain(void)
f201f309 923{
924 fqdncache_entry *i;
925 dnsserver_t *dnsData;
926 if (!fqdncacheQueueHead)
a8f7d3ee 927 return 0;
f201f309 928 while ((dnsData = dnsGetFirstAvailable()) && (i = fqdncacheDequeue()))
a8f7d3ee 929 fqdncache_dnsDispatch(dnsData, i);
f201f309 930 return 1;
931}
56e15c50 932
429fdbec 933static void
934fqdncacheLockEntry(fqdncache_entry * f)
935{
936 f->locks++;
937}
938
939static void
940fqdncacheUnlockEntry(fqdncache_entry * f)
941{
942 if (f->locks == 0) {
943 debug_trap("fqdncacheUnlockEntry: Entry has no locks");
944 return;
945 }
946 f->locks--;
947 if (fqdncacheExpiredEntry(f))
948 fqdncache_release(f);
949}
950
56e15c50 951void
952fqdncacheFreeMemory(void)
953{
954 fqdncache_entry *f;
955 fqdncache_entry **list;
c7433536 956 int i = 0;
957 int j = 0;
56e15c50 958 int k = 0;
56e15c50 959 list = xcalloc(meta_data.fqdncache_count, sizeof(fqdncache_entry *));
960 f = (fqdncache_entry *) hash_first(fqdn_table);
c7433536 961 while (f && i < meta_data.fqdncache_count) {
f6610c4e 962 *(list + i) = f;
963 i++;
964 f = (fqdncache_entry *) hash_next(fqdn_table);
56e15c50 965 }
c7433536 966 for (j = 0; j < i; j++) {
f6610c4e 967 f = *(list + j);
968 for (k = 0; k < (int) f->name_count; k++)
969 safe_free(f->names[k]);
970 safe_free(f->name);
971 safe_free(f->error_message);
972 safe_free(f);
56e15c50 973 }
974 xfree(list);
975 hashFreeMemory(fqdn_table);
976}
429fdbec 977
978static void
979fqdncacheChangeKey(fqdncache_entry * f)
980{
981 static int index = 0;
982 LOCAL_ARRAY(char, new_key, 256);
983 hash_link *table_entry = hash_lookup(fqdn_table, f->name);
984 if (table_entry == NULL) {
a3d5953d 985 debug(14, 0) ("fqdncacheChangeKey: Could not find key '%s'\n", f->name);
429fdbec 986 return;
987 }
988 if (f != (fqdncache_entry *) table_entry) {
989 debug_trap("fqdncacheChangeKey: f != table_entry!");
990 return;
991 }
992 if (hash_remove_link(fqdn_table, table_entry)) {
993 debug_trap("fqdncacheChangeKey: hash_remove_link() failed\n");
994 return;
995 }
996 sprintf(new_key, "%d/", ++index);
997 strncat(new_key, f->name, 128);
a3d5953d 998 debug(14, 1) ("fqdncacheChangeKey: from '%s' to '%s'\n", f->name, new_key);
429fdbec 999 safe_free(f->name);
1000 f->name = xstrdup(new_key);
1001 fqdncache_add_to_hash(f);
1002}
1003
1004/* call during reconfigure phase to clear out all the
1005 * pending and dispatched reqeusts that got lost */
1006void
1007fqdncache_restart(void)
1008{
1009 fqdncache_entry *this;
1010 fqdncache_entry *next;
1011 if (fqdn_table == 0)
1012 fatal_dump("fqdncache_restart: fqdn_table == 0\n");
1013 while (fqdncacheDequeue());
1014 next = (fqdncache_entry *) hash_first(fqdn_table);
1015 while ((this = next)) {
1016 next = (fqdncache_entry *) hash_next(fqdn_table);
1017 if (this->status == FQDN_CACHED)
1018 continue;
1019 if (this->status == FQDN_NEGATIVE_CACHED)
1020 continue;
1021 /* else its PENDING or DISPATCHED; there are no dnsservers
1022 * running, so abort it */
1023 this->status = FQDN_NEGATIVE_CACHED;
1024 fqdncache_release(this);
1025 }
1026}