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