]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ipcache.cc
LINT
[thirdparty/squid.git] / src / ipcache.cc
1
2 /*
3 * $Id: ipcache.cc,v 1.141 1997/11/05 05:29:30 wessels Exp $
4 *
5 * DEBUG: section 14 IP Cache
6 * AUTHOR: Harvest Derived
7 *
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
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 struct _ip_pending {
110 IPH *handler;
111 void *handlerData;
112 struct _ip_pending *next;
113 };
114
115 struct ipcacheQueueData {
116 struct ipcacheQueueData *next;
117 ipcache_entry *i;
118 };
119
120 static struct {
121 int requests;
122 int replies;
123 int hits;
124 int misses;
125 int pending_hits;
126 int negative_hits;
127 int errors;
128 int avg_svc_time;
129 int ghbn_calls; /* # calls to blocking gethostbyname() */
130 int release_locked;
131 } IpcacheStats;
132
133 static int ipcache_testname(void);
134 static QS ipcache_compareLastRef;
135 static QS ipcache_reverseLastRef;
136 static PF ipcache_dnsHandleRead;
137 static ipcache_entry *ipcache_parsebuffer(const char *buf, dnsserver_t *);
138 static void ipcache_release(ipcache_entry *);
139 static ipcache_entry *ipcache_GetFirst(void);
140 static ipcache_entry *ipcache_GetNext(void);
141 static ipcache_entry *ipcache_create(const char *name);
142 static void ipcache_add_to_hash(ipcache_entry *);
143 static void ipcache_call_pending(ipcache_entry *);
144 static ipcache_entry *ipcacheAddNew(const char *, const struct hostent *, ipcache_status_t);
145 static void ipcacheAddHostent(ipcache_entry *, const struct hostent *);
146 static int ipcacheHasPending(ipcache_entry *);
147 static ipcache_entry *ipcache_get(const char *);
148 static IPH dummy_handler;
149 static int ipcacheExpiredEntry(ipcache_entry *);
150 static void ipcacheAddPending(ipcache_entry *, IPH *, void *);
151 static void ipcacheEnqueue(ipcache_entry *);
152 static void *ipcacheDequeue(void);
153 static void ipcache_dnsDispatch(dnsserver_t *, ipcache_entry *);
154 static void ipcacheStatPrint(ipcache_entry *, StoreEntry *);
155 static void ipcacheUnlockEntry(ipcache_entry *);
156 static void ipcacheLockEntry(ipcache_entry *);
157 static void ipcacheNudgeQueue(void);
158 static void ipcacheChangeKey(ipcache_entry * i);
159
160 static ipcache_addrs static_addrs;
161 static hash_table *ip_table = NULL;
162 static struct ipcacheQueueData *ipcacheQueueHead = NULL;
163 static struct ipcacheQueueData **ipcacheQueueTailP = &ipcacheQueueHead;
164 static int queue_length = 0;
165
166 static char ipcache_status_char[] =
167 {
168 'C',
169 'N',
170 'P',
171 'D'
172 };
173
174 static long ipcache_low = 180;
175 static long ipcache_high = 200;
176
177 #if LIBRESOLV_DNS_TTL_HACK
178 extern int _dns_ttl_;
179 #endif
180
181 static void
182 ipcacheEnqueue(ipcache_entry * i)
183 {
184 static time_t last_warning = 0;
185 struct ipcacheQueueData *new = xcalloc(1, sizeof(struct ipcacheQueueData));
186 new->i = i;
187 *ipcacheQueueTailP = new;
188 ipcacheQueueTailP = &new->next;
189 queue_length++;
190 if (queue_length < NDnsServersAlloc)
191 return;
192 if (squid_curtime - last_warning < 600)
193 return;
194 last_warning = squid_curtime;
195 debug(14, 1) ("ipcacheEnqueue: WARNING: All dnsservers are busy.\n");
196 debug(14, 1) ("ipcacheEnqueue: WARNING: %d DNS lookups queued\n", queue_length);
197 if (Config.dnsChildren >= DefaultDnsChildrenMax)
198 return;
199 debug(14, 1) ("ipcacheEnqueue: Consider increasing 'dns_children' in your config file.\n");
200 }
201
202 static void *
203 ipcacheDequeue(void)
204 {
205 struct ipcacheQueueData *old = NULL;
206 ipcache_entry *i = NULL;
207 if (ipcacheQueueHead) {
208 i = ipcacheQueueHead->i;
209 old = ipcacheQueueHead;
210 ipcacheQueueHead = ipcacheQueueHead->next;
211 if (ipcacheQueueHead == NULL)
212 ipcacheQueueTailP = &ipcacheQueueHead;
213 safe_free(old);
214 queue_length--;
215 }
216 if (i != NULL)
217 assert(i->status == IP_PENDING);
218 return i;
219 }
220
221 static int
222 ipcache_testname(void)
223 {
224 wordlist *w = NULL;
225 debug(14, 1) ("Performing DNS Tests...\n");
226 if ((w = Config.dns_testname_list) == NULL)
227 return 1;
228 for (; w; w = w->next) {
229 IpcacheStats.ghbn_calls++;
230 if (gethostbyname(w->key) != NULL)
231 return 1;
232 }
233 return 0;
234 }
235
236 /* removes the given ipcache entry */
237 static void
238 ipcache_release(ipcache_entry * i)
239 {
240 hash_link *table_entry = NULL;
241 if ((table_entry = hash_lookup(ip_table, i->name)) == NULL) {
242 debug(14, 0) ("ipcache_release: Could not find key '%s'\n", i->name);
243 return;
244 }
245 assert(i == (ipcache_entry *) table_entry);
246 if (i->locks) {
247 i->expires = squid_curtime;
248 ipcacheChangeKey(i);
249 IpcacheStats.release_locked++;
250 return;
251 }
252 if (hash_remove_link(ip_table, table_entry)) {
253 debug(14, 0) ("ipcache_release: hash_remove_link() failed for '%s'\n",
254 i->name);
255 return;
256 }
257 if (i->status == IP_CACHED) {
258 safe_free(i->addrs.in_addrs);
259 safe_free(i->addrs.bad_mask);
260 debug(14, 5) ("ipcache_release: Released IP cached record for '%s'.\n",
261 i->name);
262 }
263 safe_free(i->name);
264 safe_free(i->error_message);
265 safe_free(i);
266 --meta_data.ipcache_count;
267 return;
268 }
269
270 /* return match for given name */
271 static ipcache_entry *
272 ipcache_get(const char *name)
273 {
274 hash_link *e;
275 static ipcache_entry *i;
276
277 i = NULL;
278 if (ip_table) {
279 if ((e = hash_lookup(ip_table, name)) != NULL)
280 i = (ipcache_entry *) e;
281 }
282 return i;
283 }
284
285 /* get the first ip entry in the storage */
286 static ipcache_entry *
287 ipcache_GetFirst(void)
288 {
289 return (ipcache_entry *) hash_first(ip_table);
290 }
291
292 /* get the next ip entry in the storage for a given search pointer */
293 static ipcache_entry *
294 ipcache_GetNext(void)
295 {
296 return (ipcache_entry *) hash_next(ip_table);
297 }
298
299 static int
300 ipcache_compareLastRef(const void *A, const void *B)
301 {
302 const ipcache_entry *const *e1 = A;
303 const ipcache_entry *const *e2 = B;
304 assert(e1 != NULL && e2 != NULL);
305 if ((*e1)->lastref > (*e2)->lastref)
306 return (1);
307 if ((*e1)->lastref < (*e2)->lastref)
308 return (-1);
309 return (0);
310 }
311
312 static int
313 ipcache_reverseLastRef(const void *A, const void *B)
314 {
315 const ipcache_entry *const *e1 = A;
316 const ipcache_entry *const *e2 = B;
317 assert(e1 != NULL && e2 != NULL);
318 if ((*e1)->lastref < (*e2)->lastref)
319 return (1);
320 if ((*e1)->lastref > (*e2)->lastref)
321 return (-1);
322 return (0);
323 }
324
325 static int
326 ipcacheExpiredEntry(ipcache_entry * i)
327 {
328 if (i->status == IP_PENDING)
329 return 0;
330 if (i->status == IP_DISPATCHED)
331 return 0;
332 if (i->locks != 0)
333 return 0;
334 if (i->addrs.count == 0)
335 return 1;
336 if (i->expires > squid_curtime)
337 return 0;
338 return 1;
339 }
340
341 /* finds the LRU and deletes */
342 void
343 ipcache_purgelru(void *voidnotused)
344 {
345 ipcache_entry *i = NULL;
346 int local_ip_notpending_count = 0;
347 int removed = 0;
348 int k;
349 ipcache_entry **LRU_list = NULL;
350 int LRU_list_count = 0;
351
352 eventAdd("ipcache_purgelru", ipcache_purgelru, NULL, 10);
353 LRU_list = xcalloc(meta_data.ipcache_count, sizeof(ipcache_entry *));
354
355 for (i = ipcache_GetFirst(); i; i = ipcache_GetNext()) {
356 if (ipcacheExpiredEntry(i)) {
357 ipcache_release(i);
358 removed++;
359 continue;
360 }
361 if (LRU_list_count == meta_data.ipcache_count)
362 break;
363 if (i->status == IP_PENDING)
364 continue;
365 if (i->status == IP_DISPATCHED)
366 continue;
367 if (i->locks != 0)
368 continue;
369 local_ip_notpending_count++;
370 LRU_list[LRU_list_count++] = i;
371 }
372
373 /* sort LRU candidate list */
374 qsort((char *) LRU_list,
375 LRU_list_count,
376 sizeof(ipcache_entry *),
377 ipcache_compareLastRef);
378 for (k = 0; k < LRU_list_count; k++) {
379 if (meta_data.ipcache_count < ipcache_low)
380 break;
381 if (LRU_list[k] == NULL)
382 break;
383 ipcache_release(LRU_list[k]);
384 removed++;
385 }
386 assert(meta_data.ipcache_count <= ipcache_low);
387 debug(14, 3) ("ipcache_purgelru: removed %d entries\n", removed);
388 safe_free(LRU_list);
389 }
390
391
392 /* create blank ipcache_entry */
393 static ipcache_entry *
394 ipcache_create(const char *name)
395 {
396 static ipcache_entry *new;
397 if (meta_data.ipcache_count > ipcache_high)
398 ipcache_purgelru(NULL);
399 meta_data.ipcache_count++;
400 new = xcalloc(1, sizeof(ipcache_entry));
401 new->name = xstrdup(name);
402 new->expires = squid_curtime + Config.negativeDnsTtl;
403 ipcache_add_to_hash(new);
404 return new;
405 }
406
407 static void
408 ipcache_add_to_hash(ipcache_entry * i)
409 {
410 if (hash_join(ip_table, (hash_link *) i)) {
411 debug(14, 1) ("ipcache_add_to_hash: Cannot add %s (%p) to hash table %d.\n",
412 i->name, i, ip_table);
413 }
414 debug(14, 5) ("ipcache_add_to_hash: name <%s>\n", i->name);
415 }
416
417 static void
418 ipcacheAddHostent(ipcache_entry * i, const struct hostent *hp)
419 {
420 int addr_count = 0;
421 int k;
422 safe_free(i->addrs.in_addrs);
423 safe_free(i->addrs.bad_mask);
424 while ((addr_count < 255) && *(hp->h_addr_list + addr_count))
425 ++addr_count;
426 i->addrs.count = (unsigned char) addr_count;
427 i->addrs.in_addrs = xcalloc(addr_count, sizeof(struct in_addr));
428 i->addrs.bad_mask = xcalloc(addr_count, sizeof(unsigned char));
429 i->addrs.badcount = 0;
430 for (k = 0; k < addr_count; k++)
431 xmemcpy(&i->addrs.in_addrs[k].s_addr,
432 *(hp->h_addr_list + k),
433 hp->h_length);
434 }
435
436 static ipcache_entry *
437 ipcacheAddNew(const char *name, const struct hostent *hp, ipcache_status_t status)
438 {
439 ipcache_entry *i;
440 if (ipcache_get(name))
441 fatal_dump("ipcacheAddNew: somebody adding a duplicate!");
442 debug(14, 10) ("ipcacheAddNew: Adding '%s', status=%c\n",
443 name,
444 ipcache_status_char[status]);
445 i = ipcache_create(name);
446 if (hp)
447 ipcacheAddHostent(i, hp);
448 i->status = status;
449 i->lastref = squid_curtime;
450 return i;
451 }
452
453 /* walks down the pending list, calling handlers */
454 static void
455 ipcache_call_pending(ipcache_entry * i)
456 {
457 struct _ip_pending *p = NULL;
458 int nhandler = 0;
459 i->lastref = squid_curtime;
460 ipcacheLockEntry(i);
461 while (i->pending_head != NULL) {
462 p = i->pending_head;
463 i->pending_head = p->next;
464 if (p->handler) {
465 nhandler++;
466 dns_error_message = i->error_message;
467 if (cbdataValid(p->handlerData)) {
468 p->handler(i->status == IP_CACHED ? &i->addrs : NULL,
469 p->handlerData);
470 }
471 cbdataUnlock(p->handlerData);
472 }
473 safe_free(p);
474 }
475 i->pending_head = NULL; /* nuke list */
476 debug(14, 10) ("ipcache_call_pending: Called %d handlers.\n", nhandler);
477 ipcacheUnlockEntry(i);
478 }
479
480
481 static ipcache_entry *
482 ipcache_parsebuffer(const char *inbuf, dnsserver_t * dnsData)
483 {
484 char *buf = xstrdup(inbuf);
485 char *token;
486 static ipcache_entry i;
487 int k;
488 int ipcount;
489 int aliascount;
490 debug(14, 5) ("ipcache_parsebuffer: parsing:\n%s", inbuf);
491 memset(&i, '\0', sizeof(ipcache_entry));
492 i.expires = squid_curtime + Config.positiveDnsTtl;
493 for (token = strtok(buf, w_space); token; token = strtok(NULL, w_space)) {
494 if (!strcmp(token, "$end")) {
495 break;
496 } else if (!strcmp(token, "$alive")) {
497 dnsData->answer = squid_curtime;
498 } else if (!strcmp(token, "$fail")) {
499 if ((token = strtok(NULL, "\n")) == NULL)
500 fatal_dump("Invalid $fail");
501 i.expires = squid_curtime + Config.negativeDnsTtl;
502 i.status = IP_NEGATIVE_CACHED;
503 } else if (!strcmp(token, "$message")) {
504 if ((token = strtok(NULL, "\n")) == NULL)
505 fatal_dump("Invalid $message");
506 i.error_message = xstrdup(token);
507 } else if (!strcmp(token, "$name")) {
508 if ((token = strtok(NULL, w_space)) == NULL)
509 fatal_dump("Invalid $name");
510 i.status = IP_CACHED;
511 } else if (!strcmp(token, "$h_name")) {
512 if ((token = strtok(NULL, w_space)) == NULL)
513 fatal_dump("Invalid $h_name");
514 /* ignore $h_name */
515 } else if (!strcmp(token, "$h_len")) {
516 if ((token = strtok(NULL, w_space)) == NULL)
517 fatal_dump("Invalid $h_len");
518 /* ignore $h_length */
519 } else if (!strcmp(token, "$ipcount")) {
520 if ((token = strtok(NULL, w_space)) == NULL)
521 fatal_dump("Invalid $ipcount");
522 ipcount = atoi(token);
523 i.addrs.count = (unsigned char) ipcount;
524 if (ipcount == 0) {
525 i.addrs.in_addrs = NULL;
526 i.addrs.bad_mask = NULL;
527 } else {
528 i.addrs.in_addrs = xcalloc(ipcount, sizeof(struct in_addr));
529 i.addrs.bad_mask = xcalloc(ipcount, sizeof(unsigned char));
530 }
531 for (k = 0; k < ipcount; k++) {
532 if ((token = strtok(NULL, w_space)) == NULL)
533 fatal_dump("Invalid IP address");
534 if (!safe_inet_addr(token, &i.addrs.in_addrs[k]))
535 fatal_dump("Invalid IP address");
536 }
537 } else if (!strcmp(token, "$aliascount")) {
538 if ((token = strtok(NULL, w_space)) == NULL)
539 fatal_dump("Invalid $aliascount");
540 aliascount = atoi(token);
541 for (k = 0; k < aliascount; k++) {
542 if ((token = strtok(NULL, w_space)) == NULL)
543 fatal_dump("Invalid alias");
544 }
545 } else if (!strcmp(token, "$ttl")) {
546 if ((token = strtok(NULL, w_space)) == NULL)
547 fatal_dump("Invalid $ttl");
548 i.expires = squid_curtime + atoi(token);
549 } else {
550 debug(14, 0) ("--> %s <--\n", inbuf);
551 debug_trap("Invalid dnsserver output");
552 }
553 }
554 xfree(buf);
555 return &i;
556 }
557
558 static void
559 ipcacheNudgeQueue(void)
560 {
561 dnsserver_t *dnsData;
562 ipcache_entry *i = NULL;
563 while ((dnsData = dnsGetFirstAvailable()) && (i = ipcacheDequeue()))
564 ipcache_dnsDispatch(dnsData, i);
565 }
566
567 static void
568 ipcache_dnsHandleRead(int fd, void *data)
569 {
570 dnsserver_t *dnsData = data;
571 int len;
572 int n;
573 ipcache_entry *i = NULL;
574 ipcache_entry *x = NULL;
575
576 len = read(fd,
577 dnsData->ip_inbuf + dnsData->offset,
578 dnsData->size - dnsData->offset);
579 fd_bytes(fd, len, FD_READ);
580 debug(14, 5) ("ipcache_dnsHandleRead: Result from DNS ID %d (%d bytes)\n",
581 dnsData->id, len);
582 if (len <= 0) {
583 if (len < 0 && (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR)) {
584 commSetSelect(fd,
585 COMM_SELECT_READ,
586 ipcache_dnsHandleRead,
587 dnsData,
588 0);
589 return;
590 }
591 debug(14, dnsData->flags & DNS_FLAG_CLOSING ? 5 : 1)
592 ("FD %d: Connection from DNSSERVER #%d is closed, disabling\n",
593 fd, dnsData->id);
594 dnsData->flags = 0;
595 commSetSelect(fd,
596 COMM_SELECT_WRITE,
597 NULL,
598 NULL, 0);
599 comm_close(fd);
600 return;
601 }
602 n = ++IpcacheStats.replies;
603 DnsStats.replies++;
604 dnsData->offset += len;
605 dnsData->ip_inbuf[dnsData->offset] = '\0';
606 i = dnsData->data;
607 if (i == NULL) {
608 debug_trap("NULL ipcache_entry");
609 return;
610 }
611 if (i->status != IP_DISPATCHED)
612 fatal_dump("ipcache_dnsHandleRead: bad status");
613 if (strstr(dnsData->ip_inbuf, "$end\n")) {
614 /* end of record found */
615 IpcacheStats.avg_svc_time = intAverage(IpcacheStats.avg_svc_time,
616 tvSubMsec(dnsData->dispatch_time, current_time),
617 n, IPCACHE_AV_FACTOR);
618 if ((x = ipcache_parsebuffer(dnsData->ip_inbuf, dnsData)) == NULL) {
619 debug(14, 0) ("ipcache_dnsHandleRead: ipcache_parsebuffer failed?!\n");
620 } else {
621 dnsData->offset = 0;
622 dnsData->ip_inbuf[0] = '\0';
623 i->addrs = x->addrs;
624 i->error_message = x->error_message;
625 i->status = x->status;
626 i->expires = x->expires;
627 ipcache_call_pending(i);
628 }
629 ipcacheUnlockEntry(i); /* unlock from IP_DISPATCHED */
630 } else {
631 debug(14, 5) ("ipcache_dnsHandleRead: Incomplete reply\n");
632 commSetSelect(fd,
633 COMM_SELECT_READ,
634 ipcache_dnsHandleRead,
635 dnsData,
636 0);
637 }
638 if (dnsData->offset == 0) {
639 dnsData->data = NULL;
640 dnsData->flags &= ~DNS_FLAG_BUSY;
641 }
642 ipcacheNudgeQueue();
643 }
644
645 static void
646 ipcacheAddPending(ipcache_entry * i, IPH * handler, void *handlerData)
647 {
648 struct _ip_pending *pending = xcalloc(1, sizeof(struct _ip_pending));
649 struct _ip_pending **I = NULL;
650 i->lastref = squid_curtime;
651 pending->handler = handler;
652 pending->handlerData = handlerData;
653 cbdataLock(handlerData);
654 for (I = &(i->pending_head); *I; I = &((*I)->next));
655 *I = pending;
656 if (i->status == IP_PENDING)
657 ipcacheNudgeQueue();
658 }
659
660 void
661 ipcache_nbgethostbyname(const char *name, IPH * handler, void *handlerData)
662 {
663 ipcache_entry *i = NULL;
664 dnsserver_t *dnsData = NULL;
665 const ipcache_addrs *addrs = NULL;
666
667 if (!handler)
668 fatal_dump("ipcache_nbgethostbyname: NULL handler");
669
670 debug(14, 4) ("ipcache_nbgethostbyname: Name '%s'.\n", name);
671 IpcacheStats.requests++;
672
673 if (name == NULL || name[0] == '\0') {
674 debug(14, 4) ("ipcache_nbgethostbyname: Invalid name!\n");
675 handler(NULL, handlerData);
676 return;
677 }
678 if ((addrs = ipcacheCheckNumeric(name))) {
679 handler(addrs, handlerData);
680 return;
681 }
682 if ((i = ipcache_get(name))) {
683 if (ipcacheExpiredEntry(i)) {
684 ipcache_release(i);
685 i = NULL;
686 }
687 }
688 if (i == NULL) {
689 /* MISS: No entry, create the new one */
690 debug(14, 5) ("ipcache_nbgethostbyname: MISS for '%s'\n", name);
691 IpcacheStats.misses++;
692 i = ipcacheAddNew(name, NULL, IP_PENDING);
693 ipcacheAddPending(i, handler, handlerData);
694 } else if (i->status == IP_CACHED || i->status == IP_NEGATIVE_CACHED) {
695 /* HIT */
696 debug(14, 4) ("ipcache_nbgethostbyname: HIT for '%s'\n", name);
697 if (i->status == IP_NEGATIVE_CACHED)
698 IpcacheStats.negative_hits++;
699 else
700 IpcacheStats.hits++;
701 ipcacheAddPending(i, handler, handlerData);
702 ipcache_call_pending(i);
703 return;
704 } else if (i->status == IP_PENDING || i->status == IP_DISPATCHED) {
705 debug(14, 4) ("ipcache_nbgethostbyname: PENDING for '%s'\n", name);
706 IpcacheStats.pending_hits++;
707 ipcacheAddPending(i, handler, handlerData);
708 if (squid_curtime - i->expires > 600) {
709 debug(14, 0) ("ipcache_nbgethostbyname: '%s' PENDING for %d seconds, aborting\n", name, squid_curtime + Config.negativeDnsTtl - i->expires);
710 ipcacheChangeKey(i);
711 ipcache_call_pending(i);
712 }
713 return;
714 } else {
715 fatal_dump("ipcache_nbgethostbyname: BAD ipcache_entry status");
716 }
717
718 /* for HIT, PENDING, DISPATCHED we've returned. For MISS we continue */
719
720 if ((dnsData = dnsGetFirstAvailable())) {
721 ipcache_dnsDispatch(dnsData, i);
722 } else if (NDnsServersAlloc) {
723 ipcacheEnqueue(i);
724 } else {
725 /* generate abort if we get here */
726 assert(NDnsServersAlloc);
727 }
728 }
729
730 static void
731 ipcache_dnsDispatch(dnsserver_t * dns, ipcache_entry * i)
732 {
733 char *buf = NULL;
734 assert(BIT_TEST(dns->flags, DNS_FLAG_ALIVE));
735 if (!ipcacheHasPending(i)) {
736 debug(14, 0) ("Skipping lookup of '%s' because client(s) disappeared.\n",
737 i->name);
738 i->status = IP_NEGATIVE_CACHED;
739 ipcache_release(i);
740 return;
741 }
742 assert(i->status == IP_PENDING);
743 buf = xcalloc(1, 256);
744 snprintf(buf, 256, "%s\n", i->name);
745 dns->flags |= DNS_FLAG_BUSY;
746 dns->data = i;
747 i->status = IP_DISPATCHED;
748 comm_write(dns->outpipe,
749 buf,
750 strlen(buf),
751 NULL, /* Handler */
752 NULL, /* Handler-data */
753 xfree);
754 commSetSelect(dns->outpipe,
755 COMM_SELECT_READ,
756 ipcache_dnsHandleRead,
757 dns, 0);
758 debug(14, 5) ("ipcache_dnsDispatch: Request sent to DNS server #%d.\n",
759 dns->id);
760 dns->dispatch_time = current_time;
761 DnsStats.requests++;
762 DnsStats.hist[dns->id - 1]++;
763 ipcacheLockEntry(i); /* lock while IP_DISPATCHED */
764 }
765
766
767 /* initialize the ipcache */
768 void
769 ipcache_init(void)
770 {
771 debug(14, 3) ("Initializing IP Cache...\n");
772
773 memset(&IpcacheStats, '\0', sizeof(IpcacheStats));
774
775 /* test naming lookup */
776 if (!opt_dns_tests) {
777 debug(14, 4) ("ipcache_init: Skipping DNS name lookup tests.\n");
778 } else if (!ipcache_testname()) {
779 fatal("ipcache_init: DNS name lookup tests failed.");
780 } else {
781 debug(14, 1) ("Successful DNS name lookup tests...\n");
782 }
783
784 ip_table = hash_create(urlcmp, 229, hash4); /* small hash table */
785 memset(&static_addrs, '\0', sizeof(ipcache_addrs));
786 static_addrs.in_addrs = xcalloc(1, sizeof(struct in_addr));
787 static_addrs.bad_mask = xcalloc(1, sizeof(unsigned char));
788
789 ipcache_high = (long) (((float) Config.ipcache.size *
790 (float) Config.ipcache.high) / (float) 100);
791 ipcache_low = (long) (((float) Config.ipcache.size *
792 (float) Config.ipcache.low) / (float) 100);
793 }
794
795 int
796 ipcacheUnregister(const char *name, void *data)
797 {
798 ipcache_entry *i = NULL;
799 struct _ip_pending *p = NULL;
800 int n = 0;
801 debug(14, 3) ("ipcacheUnregister: name '%s'\n", name);
802 if ((i = ipcache_get(name)) == NULL)
803 return 0;
804 if (i->status == IP_PENDING || i->status == IP_DISPATCHED) {
805 for (p = i->pending_head; p; p = p->next) {
806 if (p->handlerData != data)
807 continue;
808 p->handler = NULL;
809 n++;
810 }
811 }
812 assert(n > 0);
813 debug(14, 3) ("ipcacheUnregister: unregistered %d handlers\n", n);
814 return n;
815 }
816
817 const ipcache_addrs *
818 ipcache_gethostbyname(const char *name, int flags)
819 {
820 ipcache_entry *i = NULL;
821 ipcache_addrs *addrs;
822
823 if (!name)
824 fatal_dump("ipcache_gethostbyname: NULL name");
825 debug(14, 3) ("ipcache_gethostbyname: '%s', flags=%x\n", name, flags);
826 IpcacheStats.requests++;
827 if ((i = ipcache_get(name))) {
828 if (ipcacheExpiredEntry(i)) {
829 ipcache_release(i);
830 i = NULL;
831 }
832 }
833 if (i) {
834 if (i->status == IP_NEGATIVE_CACHED) {
835 IpcacheStats.negative_hits++;
836 dns_error_message = i->error_message;
837 return NULL;
838 } else {
839 IpcacheStats.hits++;
840 i->lastref = squid_curtime;
841 return &i->addrs;
842 }
843 }
844 if ((addrs = ipcacheCheckNumeric(name)))
845 return addrs;
846 IpcacheStats.misses++;
847 if (flags & IP_LOOKUP_IF_MISS)
848 ipcache_nbgethostbyname(name, dummy_handler, NULL);
849 return NULL;
850 }
851
852 static void
853 ipcacheStatPrint(ipcache_entry * i, StoreEntry * sentry)
854 {
855 int k;
856 storeAppendPrintf(sentry, " {%-32.32s %c%c %6d %6d %2d(%2d)",
857 i->name,
858 ipcache_status_char[i->status],
859 i->locks ? 'L' : ' ',
860 (int) (squid_curtime - i->lastref),
861 (int) (i->expires - squid_curtime),
862 (int) i->addrs.count,
863 (int) i->addrs.badcount);
864 for (k = 0; k < (int) i->addrs.count; k++)
865 storeAppendPrintf(sentry, " %15s-%3s", inet_ntoa(i->addrs.in_addrs[k]),
866 i->addrs.bad_mask[k] ? "BAD" : "OK ");
867 storeAppendPrintf(sentry, close_bracket);
868 }
869
870 /* process objects list */
871 void
872 stat_ipcache_get(StoreEntry * sentry)
873 {
874 int k;
875 int N;
876 ipcache_entry *i = NULL;
877 ipcache_entry **list = NULL;
878 if (!ip_table)
879 return;
880 storeAppendPrintf(sentry, "{IP Cache Statistics:\n");
881 storeAppendPrintf(sentry, "{IPcache Entries: %d}\n",
882 meta_data.ipcache_count);
883 storeAppendPrintf(sentry, "{IPcache Requests: %d}\n",
884 IpcacheStats.requests);
885 storeAppendPrintf(sentry, "{IPcache Hits: %d}\n",
886 IpcacheStats.hits);
887 storeAppendPrintf(sentry, "{IPcache Pending Hits: %d}\n",
888 IpcacheStats.pending_hits);
889 storeAppendPrintf(sentry, "{IPcache Negative Hits: %d}\n",
890 IpcacheStats.negative_hits);
891 storeAppendPrintf(sentry, "{IPcache Misses: %d}\n",
892 IpcacheStats.misses);
893 storeAppendPrintf(sentry, "{Blocking calls to gethostbyname(): %d}\n",
894 IpcacheStats.ghbn_calls);
895 storeAppendPrintf(sentry, "{Attempts to release locked entries: %d}\n",
896 IpcacheStats.release_locked);
897 storeAppendPrintf(sentry, "{dnsserver avg service time: %d msec}\n",
898 IpcacheStats.avg_svc_time);
899 storeAppendPrintf(sentry, "{pending queue length: %d}\n", queue_length);
900 storeAppendPrintf(sentry, "}\n\n");
901 storeAppendPrintf(sentry, "{IP Cache Contents:\n\n");
902 storeAppendPrintf(sentry, " {%-29.29s %5s %6s %6s %1s}\n",
903 "Hostname",
904 "Flags",
905 "lstref",
906 "TTL",
907 "N");
908 list = xcalloc(meta_data.ipcache_count, sizeof(ipcache_entry *));
909 N = 0;
910 for (i = ipcache_GetFirst(); i; i = ipcache_GetNext()) {
911 *(list + N) = i;
912 assert(++N <= meta_data.ipcache_count);
913 }
914 qsort((char *) list,
915 N,
916 sizeof(ipcache_entry *),
917 ipcache_reverseLastRef);
918 for (k = 0; k < N; k++)
919 ipcacheStatPrint(*(list + k), sentry);
920 storeAppendPrintf(sentry, close_bracket);
921 xfree(list);
922 }
923
924 static void
925 dummy_handler(const ipcache_addrs * addrsnotused, void *datanotused)
926 {
927 return;
928 }
929
930 static int
931 ipcacheHasPending(ipcache_entry * i)
932 {
933 struct _ip_pending *p = NULL;
934 if (i->status != IP_PENDING)
935 return 0;
936 for (p = i->pending_head; p; p = p->next)
937 if (p->handler)
938 return 1;
939 return 0;
940 }
941
942 void
943 ipcacheReleaseInvalid(const char *name)
944 {
945 ipcache_entry *i;
946 if ((i = ipcache_get(name)) == NULL)
947 return;
948 if (i->status != IP_NEGATIVE_CACHED)
949 return;
950 ipcache_release(i);
951 }
952
953 void
954 ipcacheInvalidate(const char *name)
955 {
956 ipcache_entry *i;
957 if ((i = ipcache_get(name)) == NULL)
958 return;
959 i->expires = squid_curtime;
960 /* NOTE, don't call ipcache_release here becuase we might be here due
961 * to a thread started from ipcache_call_pending() which will cause a
962 * FMR */
963 }
964
965 ipcache_addrs *
966 ipcacheCheckNumeric(const char *name)
967 {
968 struct in_addr ip;
969 /* check if it's already a IP address in text form. */
970 if (!safe_inet_addr(name, &ip))
971 return NULL;
972 static_addrs.count = 1;
973 static_addrs.cur = 0;
974 static_addrs.in_addrs[0].s_addr = ip.s_addr;
975 static_addrs.bad_mask[0] = FALSE;
976 static_addrs.badcount = 0;
977 return &static_addrs;
978 }
979
980 int
981 ipcacheQueueDrain(void)
982 {
983 if (!ipcacheQueueHead)
984 return 0;
985 ipcacheNudgeQueue();
986 return 1;
987 }
988
989 static void
990 ipcacheLockEntry(ipcache_entry * i)
991 {
992 i->locks++;
993 }
994
995 static void
996 ipcacheUnlockEntry(ipcache_entry * i)
997 {
998 assert(i->locks > 0);
999 i->locks--;
1000 if (ipcacheExpiredEntry(i))
1001 ipcache_release(i);
1002 }
1003
1004 void
1005 ipcacheCycleAddr(const char *name)
1006 {
1007 ipcache_entry *i;
1008 ipcache_addrs *ia;
1009 unsigned char fullcircle;
1010 if ((i = ipcache_get(name)) == NULL)
1011 return;
1012 if (i->status != IP_CACHED)
1013 return;
1014 ia = &i->addrs;
1015 fullcircle = ia->cur;
1016 while (ia->bad_mask[ia->cur]) {
1017 if (++ia->cur == ia->count)
1018 ia->cur = 0;
1019 if (ia->cur == fullcircle) { /* All bad, just use next one */
1020 if (++ia->cur == ia->count)
1021 ia->cur = 0;
1022 break;
1023 }
1024 }
1025 }
1026
1027 /* "MarkBad" function must leave the "cur" pointer at the next
1028 * available good address, or the next bad address, in the list.
1029 * This simulates the functionality of RemoveBadAddr() which it
1030 * replaces. Marking, instead of removing, allows bad addresses
1031 * to be retried as a last resort before returning an error to
1032 * the user.
1033 */
1034 void
1035 ipcacheMarkBadAddr(const char *name, struct in_addr addr)
1036 {
1037 ipcache_entry *i;
1038 ipcache_addrs *ia;
1039 int k;
1040 if ((i = ipcache_get(name)) == NULL)
1041 return;
1042 ia = &i->addrs;
1043 for (k = 0; k < (int) ia->count; k++) {
1044 if (ia->in_addrs[k].s_addr == addr.s_addr)
1045 break;
1046 }
1047 if (k == (int) ia->count)
1048 return;
1049 if (!ia->bad_mask[k]) {
1050 ia->bad_mask[k] = TRUE;
1051 ia->badcount++;
1052 debug(14, 2) ("ipcacheMarkBadAddr: %s [%s]\n",
1053 name, inet_ntoa(ia->in_addrs[k]));
1054 if (ia->badcount != ia->count) {
1055 /* at least one good address left */
1056 i->expires = squid_curtime + Config.positiveDnsTtl;
1057 while (ia->bad_mask[ia->cur])
1058 if (++ia->cur == ia->count)
1059 ia->cur = 0;
1060 return;
1061 }
1062 }
1063 if (++ia->cur == ia->count)
1064 ia->cur = 0;
1065 }
1066
1067 void
1068 ipcacheMarkGoodAddr(const char *name, struct in_addr addr)
1069 {
1070 ipcache_entry *i;
1071 ipcache_addrs *ia;
1072 int k;
1073 if ((i = ipcache_get(name)) == NULL)
1074 return;
1075 ia = &i->addrs;
1076 for (k = 0; k < (int) ia->count; k++) {
1077 if (ia->in_addrs[k].s_addr == addr.s_addr)
1078 break;
1079 }
1080 if (k == (int) ia->count)
1081 return;
1082 i->expires = squid_curtime + Config.positiveDnsTtl;
1083 if (ia->bad_mask[k]) {
1084 ia->bad_mask[k] = FALSE;
1085 ia->badcount--;
1086 i->expires = squid_curtime + Config.positiveDnsTtl;
1087 debug(14, 2) ("ipcacheMarkGoodAddr: %s [%s]\n",
1088 name, inet_ntoa(ia->in_addrs[k]));
1089 }
1090 }
1091
1092 void
1093 ipcacheFreeMemory(void)
1094 {
1095 ipcache_entry *i;
1096 ipcache_entry **list;
1097 int k = 0;
1098 int j;
1099 list = xcalloc(meta_data.ipcache_count, sizeof(ipcache_entry *));
1100 i = (ipcache_entry *) hash_first(ip_table);
1101 while (i && k < meta_data.ipcache_count) {
1102 *(list + k) = i;
1103 k++;
1104 i = (ipcache_entry *) hash_next(ip_table);
1105 }
1106 for (j = 0; j < k; j++) {
1107 i = *(list + j);
1108 safe_free(i->addrs.in_addrs);
1109 safe_free(i->addrs.bad_mask);
1110 safe_free(i->name);
1111 safe_free(i->error_message);
1112 safe_free(i);
1113 }
1114 xfree(list);
1115 hashFreeMemory(ip_table);
1116 ip_table = NULL;
1117 }
1118
1119 static void
1120 ipcacheChangeKey(ipcache_entry * i)
1121 {
1122 static int index = 0;
1123 LOCAL_ARRAY(char, new_key, 256);
1124 hash_link *table_entry = hash_lookup(ip_table, i->name);
1125 if (table_entry == NULL) {
1126 debug(14, 0) ("ipcacheChangeKey: Could not find key '%s'\n", i->name);
1127 return;
1128 }
1129 assert(i == (ipcache_entry *) table_entry);
1130 if (hash_remove_link(ip_table, table_entry)) {
1131 debug_trap("ipcacheChangeKey: hash_remove_link() failed\n");
1132 return;
1133 }
1134 snprintf(new_key, 256, "%d/", ++index);
1135 strncat(new_key, i->name, 128);
1136 debug(14, 1) ("ipcacheChangeKey: from '%s' to '%s'\n", i->name, new_key);
1137 safe_free(i->name);
1138 i->name = xstrdup(new_key);
1139 ipcache_add_to_hash(i);
1140 }
1141
1142 /* call during reconfigure phase to clear out all the
1143 * pending and dispatched reqeusts that got lost */
1144 void
1145 ipcache_restart(void)
1146 {
1147 ipcache_entry *this;
1148 ipcache_entry *next;
1149 assert(ip_table != NULL);
1150 while (ipcacheDequeue());
1151 next = (ipcache_entry *) hash_first(ip_table);
1152 while ((this = next) != NULL) {
1153 next = (ipcache_entry *) hash_next(ip_table);
1154 if (this->status == IP_CACHED)
1155 continue;
1156 if (this->status == IP_NEGATIVE_CACHED)
1157 continue;
1158 /* else its PENDING or DISPATCHED; there are no dnsservers
1159 * running, so abort it */
1160 this->status = IP_NEGATIVE_CACHED;
1161 ipcache_release(this);
1162 }
1163 /* recalculate these while we're at it */
1164 ipcache_high = (long) (((float) Config.ipcache.size *
1165 (float) Config.ipcache.high) / (float) 100);
1166 ipcache_low = (long) (((float) Config.ipcache.size *
1167 (float) Config.ipcache.low) / (float) 100);
1168 }