]>
Commit | Line | Data |
---|---|---|
f88bb09c | 1 | |
2 | /* | |
24382924 | 3 | * $Id: fqdncache.cc,v 1.22 1996/10/09 15:34:25 wessels Exp $ |
f88bb09c | 4 | * |
7cf620a9 | 5 | * DEBUG: section 35 FQDN Cache |
f88bb09c | 6 | * AUTHOR: Harvest Derived |
7 | * | |
8 | * SQUID Internet Object Cache http://www.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 | #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 | |
115 | struct _fqdn_pending { | |
116 | int fd; | |
117 | FQDNH handler; | |
118 | void *handlerData; | |
119 | struct _fqdn_pending *next; | |
120 | }; | |
121 | ||
122 | struct fqdncacheQueueData { | |
123 | struct fqdncacheQueueData *next; | |
124 | fqdncache_entry *f; | |
125 | }; | |
126 | ||
127 | static struct { | |
128 | int requests; | |
129 | int replies; | |
130 | int hits; | |
131 | int misses; | |
132 | int pending_hits; | |
133 | int negative_hits; | |
134 | int errors; | |
135 | int avg_svc_time; | |
136 | int ghba_calls; /* # calls to blocking gethostbyaddr() */ | |
137 | } FqdncacheStats; | |
138 | ||
67508012 | 139 | static int fqdncache_compareLastRef _PARAMS((fqdncache_entry **, fqdncache_entry **)); |
140 | static int fqdncache_dnsHandleRead _PARAMS((int, dnsserver_t *)); | |
141 | static fqdncache_entry *fqdncache_parsebuffer _PARAMS((char *buf, dnsserver_t *)); | |
142 | static int fqdncache_purgelru _PARAMS((void)); | |
143 | static void fqdncache_release _PARAMS((fqdncache_entry *)); | |
144 | static fqdncache_entry *fqdncache_GetFirst _PARAMS((void)); | |
145 | static fqdncache_entry *fqdncache_GetNext _PARAMS((void)); | |
146 | static fqdncache_entry *fqdncache_create _PARAMS((void)); | |
147 | static void fqdncache_add_to_hash _PARAMS((fqdncache_entry *)); | |
148 | static void fqdncache_call_pending _PARAMS((fqdncache_entry *)); | |
149 | static void fqdncache_call_pending_badname _PARAMS((int fd, FQDNH handler, void *)); | |
150 | static void fqdncache_add _PARAMS((char *, fqdncache_entry *, struct hostent *, int)); | |
151 | static int fqdncacheHasPending _PARAMS((fqdncache_entry *)); | |
152 | static fqdncache_entry *fqdncache_get _PARAMS((char *)); | |
153 | static void dummy_handler _PARAMS((int, char *, void *)); | |
154 | static int fqdncacheExpiredEntry _PARAMS((fqdncache_entry *)); | |
155 | static void fqdncacheAddPending _PARAMS((fqdncache_entry *, int fd, FQDNH, void *)); | |
156 | static void fqdncacheEnqueue _PARAMS((fqdncache_entry *)); | |
157 | static void *fqdncacheDequeue _PARAMS((void)); | |
158 | static void fqdncache_dnsDispatch _PARAMS((dnsserver_t *, fqdncache_entry *)); | |
f88bb09c | 159 | |
f88bb09c | 160 | static HashID fqdn_table = 0; |
161 | static struct fqdncacheQueueData *fqdncacheQueueHead = NULL; | |
162 | static struct fqdncacheQueueData **fqdncacheQueueTailP = &fqdncacheQueueHead; | |
163 | ||
164 | static char fqdncache_status_char[] = | |
165 | { | |
166 | 'C', | |
167 | 'N', | |
168 | 'P', | |
169 | 'D' | |
170 | }; | |
171 | ||
24382924 | 172 | static long fqdncache_low = 180; |
173 | static long fqdncache_high = 200; | |
f88bb09c | 174 | |
b8d8561b | 175 | static void |
176 | fqdncacheEnqueue(fqdncache_entry * f) | |
f88bb09c | 177 | { |
178 | struct fqdncacheQueueData *new = xcalloc(1, sizeof(struct fqdncacheQueueData)); | |
179 | new->f = f; | |
180 | *fqdncacheQueueTailP = new; | |
181 | fqdncacheQueueTailP = &new->next; | |
182 | } | |
183 | ||
b8d8561b | 184 | static void * |
0673c0ba | 185 | fqdncacheDequeue(void) |
f88bb09c | 186 | { |
187 | struct fqdncacheQueueData *old = NULL; | |
188 | fqdncache_entry *f = NULL; | |
189 | if (fqdncacheQueueHead) { | |
190 | f = fqdncacheQueueHead->f; | |
191 | old = fqdncacheQueueHead; | |
192 | fqdncacheQueueHead = fqdncacheQueueHead->next; | |
193 | if (fqdncacheQueueHead == NULL) | |
194 | fqdncacheQueueTailP = &fqdncacheQueueHead; | |
195 | safe_free(old); | |
196 | } | |
197 | return f; | |
198 | } | |
199 | ||
200 | /* removes the given fqdncache entry */ | |
b8d8561b | 201 | static void |
202 | fqdncache_release(fqdncache_entry * f) | |
f88bb09c | 203 | { |
204 | fqdncache_entry *result = NULL; | |
205 | hash_link *table_entry = NULL; | |
206 | int k; | |
207 | ||
208 | if ((table_entry = hash_lookup(fqdn_table, f->name)) == NULL) { | |
7cf620a9 | 209 | debug(35, 0, "fqdncache_release: Could not find key '%s'\n", f->name); |
f88bb09c | 210 | return; |
211 | } | |
212 | result = (fqdncache_entry *) table_entry; | |
213 | if (f != result) | |
214 | fatal_dump("fqdncache_release: expected f == result!"); | |
215 | if (f->status == FQDN_PENDING) { | |
7cf620a9 | 216 | debug(35, 1, "fqdncache_release: Someone called on a PENDING entry\n"); |
f88bb09c | 217 | return; |
218 | } | |
219 | if (f->status == FQDN_DISPATCHED) { | |
7cf620a9 | 220 | debug(35, 1, "fqdncache_release: Someone called on a DISPATCHED entry\n"); |
f88bb09c | 221 | return; |
222 | } | |
223 | if (hash_remove_link(fqdn_table, table_entry)) { | |
7cf620a9 | 224 | debug(35, 0, "fqdncache_release: hash_remove_link() failed for '%s'\n", |
f88bb09c | 225 | result->name); |
226 | return; | |
227 | } | |
228 | if (result->status == FQDN_CACHED) { | |
229 | for (k = 0; k < (int) f->name_count; k++) | |
230 | safe_free(f->names[k]); | |
7cf620a9 | 231 | debug(35, 5, "fqdncache_release: Released FQDN record for '%s'.\n", |
f88bb09c | 232 | result->name); |
233 | } | |
234 | safe_free(result->name); | |
235 | safe_free(result->error_message); | |
236 | memset(result, '\0', sizeof(fqdncache_entry)); | |
237 | safe_free(result); | |
238 | --meta_data.fqdncache_count; | |
239 | return; | |
240 | } | |
241 | ||
242 | /* return match for given name */ | |
b8d8561b | 243 | static fqdncache_entry * |
244 | fqdncache_get(char *name) | |
f88bb09c | 245 | { |
246 | hash_link *e; | |
247 | static fqdncache_entry *f; | |
248 | ||
249 | f = NULL; | |
250 | if (fqdn_table) { | |
251 | if ((e = hash_lookup(fqdn_table, name)) != NULL) | |
252 | f = (fqdncache_entry *) e; | |
253 | } | |
254 | return f; | |
255 | } | |
256 | ||
b8d8561b | 257 | static fqdncache_entry * |
0673c0ba | 258 | fqdncache_GetFirst(void) |
f88bb09c | 259 | { |
260 | return (fqdncache_entry *) hash_first(fqdn_table); | |
261 | } | |
262 | ||
b8d8561b | 263 | static fqdncache_entry * |
0673c0ba | 264 | fqdncache_GetNext(void) |
f88bb09c | 265 | { |
266 | return (fqdncache_entry *) hash_next(fqdn_table); | |
267 | } | |
268 | ||
b8d8561b | 269 | static int |
270 | fqdncache_compareLastRef(fqdncache_entry ** e1, fqdncache_entry ** e2) | |
f88bb09c | 271 | { |
272 | if (!e1 || !e2) | |
273 | fatal_dump(NULL); | |
274 | if ((*e1)->lastref > (*e2)->lastref) | |
275 | return (1); | |
276 | if ((*e1)->lastref < (*e2)->lastref) | |
277 | return (-1); | |
278 | return (0); | |
279 | } | |
280 | ||
b8d8561b | 281 | static int |
282 | fqdncacheExpiredEntry(fqdncache_entry * f) | |
f88bb09c | 283 | { |
284 | if (f->status == FQDN_PENDING) | |
285 | return 0; | |
286 | if (f->status == FQDN_DISPATCHED) | |
287 | return 0; | |
e84703ad | 288 | if (f->expires > squid_curtime) |
f88bb09c | 289 | return 0; |
290 | return 1; | |
291 | } | |
292 | ||
293 | /* finds the LRU and deletes */ | |
b8d8561b | 294 | static int |
0673c0ba | 295 | fqdncache_purgelru(void) |
f88bb09c | 296 | { |
297 | fqdncache_entry *f = NULL; | |
298 | int local_fqdn_count = 0; | |
299 | int local_fqdn_notpending_count = 0; | |
300 | int removed = 0; | |
301 | int k; | |
302 | fqdncache_entry **LRU_list = NULL; | |
303 | int LRU_list_count = 0; | |
304 | int LRU_cur_size = meta_data.fqdncache_count; | |
305 | ||
306 | LRU_list = xcalloc(LRU_cur_size, sizeof(fqdncache_entry *)); | |
307 | ||
308 | for (f = fqdncache_GetFirst(); f; f = fqdncache_GetNext()) { | |
309 | if (fqdncacheExpiredEntry(f)) { | |
310 | fqdncache_release(f); | |
311 | removed++; | |
312 | continue; | |
313 | } | |
314 | local_fqdn_count++; | |
315 | ||
316 | if (LRU_list_count >= LRU_cur_size) { | |
317 | /* have to realloc */ | |
318 | LRU_cur_size += 16; | |
7cf620a9 | 319 | debug(35, 3, "fqdncache_purgelru: Have to grow LRU_list to %d. This shouldn't happen.\n", |
f88bb09c | 320 | LRU_cur_size); |
321 | LRU_list = xrealloc((char *) LRU_list, | |
322 | LRU_cur_size * sizeof(fqdncache_entry *)); | |
323 | } | |
324 | if (f->status == FQDN_PENDING) | |
325 | continue; | |
326 | if (f->status == FQDN_DISPATCHED) | |
327 | continue; | |
328 | local_fqdn_notpending_count++; | |
329 | LRU_list[LRU_list_count++] = f; | |
330 | } | |
331 | ||
7cf620a9 | 332 | debug(35, 3, "fqdncache_purgelru: fqdncache_count: %5d\n", meta_data.fqdncache_count); |
333 | debug(35, 3, " actual count : %5d\n", local_fqdn_count); | |
334 | debug(35, 3, " high W mark : %5d\n", fqdncache_high); | |
335 | debug(35, 3, " low W mark : %5d\n", fqdncache_low); | |
336 | debug(35, 3, " not pending : %5d\n", local_fqdn_notpending_count); | |
337 | debug(35, 3, " LRU candidates : %5d\n", LRU_list_count); | |
f88bb09c | 338 | |
339 | /* sort LRU candidate list */ | |
340 | qsort((char *) LRU_list, | |
341 | LRU_list_count, | |
342 | sizeof(f), | |
343 | (int (*)(const void *, const void *)) fqdncache_compareLastRef); | |
344 | for (k = 0; LRU_list[k] && (meta_data.fqdncache_count > fqdncache_low) | |
345 | && k < LRU_list_count; | |
346 | ++k) { | |
347 | fqdncache_release(LRU_list[k]); | |
348 | removed++; | |
349 | } | |
350 | ||
7cf620a9 | 351 | debug(35, 3, " removed : %5d\n", removed); |
f88bb09c | 352 | safe_free(LRU_list); |
353 | return (removed > 0) ? 0 : -1; | |
354 | } | |
355 | ||
356 | ||
357 | /* create blank fqdncache_entry */ | |
b8d8561b | 358 | static fqdncache_entry * |
0673c0ba | 359 | fqdncache_create(void) |
f88bb09c | 360 | { |
361 | static fqdncache_entry *new; | |
362 | ||
363 | if (meta_data.fqdncache_count > fqdncache_high) { | |
364 | if (fqdncache_purgelru() < 0) | |
7cf620a9 | 365 | debug(35, 0, "HELP!! FQDN Cache is overflowing!\n"); |
f88bb09c | 366 | } |
367 | meta_data.fqdncache_count++; | |
368 | new = xcalloc(1, sizeof(fqdncache_entry)); | |
369 | return new; | |
370 | ||
371 | } | |
372 | ||
b8d8561b | 373 | static void |
374 | fqdncache_add_to_hash(fqdncache_entry * f) | |
f88bb09c | 375 | { |
376 | if (hash_join(fqdn_table, (hash_link *) f)) { | |
7cf620a9 | 377 | debug(35, 1, "fqdncache_add_to_hash: Cannot add %s (%p) to hash table %d.\n", |
f88bb09c | 378 | f->name, f, fqdn_table); |
379 | } | |
7cf620a9 | 380 | debug(35, 5, "fqdncache_add_to_hash: name <%s>\n", f->name); |
f88bb09c | 381 | } |
382 | ||
383 | ||
b8d8561b | 384 | static void |
385 | fqdncache_add(char *name, fqdncache_entry * f, struct hostent *hp, int cached) | |
f88bb09c | 386 | { |
387 | int k; | |
388 | ||
389 | if (fqdncache_get(name)) | |
390 | fatal_dump("fqdncache_add: somebody adding a duplicate!"); | |
7cf620a9 | 391 | debug(35, 10, "fqdncache_add: Adding name '%s' (%s).\n", name, |
f88bb09c | 392 | cached ? "cached" : "not cached"); |
393 | f->name = xstrdup(name); | |
394 | if (cached) { | |
395 | f->name_count = 0; | |
396 | f->names[f->name_count++] = xstrdup(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 | } | |
e84703ad | 402 | f->lastref = squid_curtime; |
f88bb09c | 403 | f->status = FQDN_CACHED; |
e84703ad | 404 | f->expires = squid_curtime + Config.positiveDnsTtl; |
f88bb09c | 405 | } else { |
e84703ad | 406 | f->lastref = squid_curtime; |
f88bb09c | 407 | f->status = FQDN_NEGATIVE_CACHED; |
e84703ad | 408 | f->expires = squid_curtime + Config.negativeDnsTtl; |
f88bb09c | 409 | } |
410 | fqdncache_add_to_hash(f); | |
411 | } | |
412 | ||
413 | /* walks down the pending list, calling handlers */ | |
b8d8561b | 414 | static void |
415 | fqdncache_call_pending(fqdncache_entry * f) | |
f88bb09c | 416 | { |
417 | struct _fqdn_pending *p = NULL; | |
418 | int nhandler = 0; | |
419 | ||
420 | f->lastref = squid_curtime; | |
421 | ||
422 | while (f->pending_head != NULL) { | |
423 | p = f->pending_head; | |
424 | f->pending_head = p->next; | |
425 | if (p->handler) { | |
426 | nhandler++; | |
427 | dns_error_message = f->error_message; | |
428 | p->handler(p->fd, | |
429 | (f->status == FQDN_CACHED) ? f->names[0] : NULL, | |
430 | p->handlerData); | |
431 | } | |
432 | memset(p, '\0', sizeof(struct _fqdn_pending)); | |
433 | safe_free(p); | |
434 | } | |
435 | f->pending_head = NULL; /* nuke list */ | |
7cf620a9 | 436 | debug(35, 10, "fqdncache_call_pending: Called %d handlers.\n", nhandler); |
f88bb09c | 437 | } |
438 | ||
b8d8561b | 439 | static void |
440 | fqdncache_call_pending_badname(int fd, FQDNH handler, void *data) | |
f88bb09c | 441 | { |
7cf620a9 | 442 | debug(35, 0, "fqdncache_call_pending_badname: Bad Name: Calling handler with NULL result.\n"); |
f88bb09c | 443 | handler(fd, NULL, data); |
444 | } | |
445 | ||
b8d8561b | 446 | static fqdncache_entry * |
447 | fqdncache_parsebuffer(char *inbuf, dnsserver_t * dnsData) | |
f88bb09c | 448 | { |
e84703ad | 449 | char *buf = xstrdup(inbuf); |
450 | char *token; | |
451 | static fqdncache_entry f; | |
452 | int k; | |
f88bb09c | 453 | int ipcount; |
454 | int aliascount; | |
e84703ad | 455 | debug(35, 5, "fqdncache_parsebuffer: parsing:\n%s", inbuf); |
456 | memset(&f, '\0', sizeof(fqdncache_entry)); | |
457 | f.expires = squid_curtime + Config.positiveDnsTtl; | |
620da955 | 458 | f.status = FQDN_DISPATCHED; |
e84703ad | 459 | for (token = strtok(buf, w_space); token; token = strtok(NULL, w_space)) { |
460 | if (!strcmp(token, "$end")) { | |
f88bb09c | 461 | break; |
e84703ad | 462 | } else if (!strcmp(token, "$alive")) { |
f88bb09c | 463 | dnsData->answer = squid_curtime; |
e84703ad | 464 | } else if (!strcmp(token, "$fail")) { |
465 | if ((token = strtok(NULL, w_space)) == NULL) | |
466 | fatal_dump("Invalid $fail"); | |
467 | f.expires = squid_curtime + Config.negativeDnsTtl; | |
620da955 | 468 | f.status = FQDN_NEGATIVE_CACHED; |
e84703ad | 469 | } else if (!strcmp(token, "$message")) { |
470 | if ((token = strtok(NULL, "\n")) == NULL) | |
471 | fatal_dump("Invalid $message"); | |
472 | f.error_message = xstrdup(token); | |
473 | } else if (!strcmp(token, "$name")) { | |
474 | if ((token = strtok(NULL, w_space)) == NULL) | |
475 | fatal_dump("Invalid $name"); | |
620da955 | 476 | f.status = FQDN_CACHED; |
e84703ad | 477 | } else if (!strcmp(token, "$h_name")) { |
478 | if ((token = strtok(NULL, w_space)) == NULL) | |
479 | fatal_dump("Invalid $h_name"); | |
480 | f.names[0] = xstrdup(token); | |
481 | f.name_count = 1; | |
482 | } else if (!strcmp(token, "$h_len")) { | |
483 | if ((token = strtok(NULL, w_space)) == NULL) | |
484 | fatal_dump("Invalid $h_len"); | |
485 | } else if (!strcmp(token, "$ipcount")) { | |
486 | if ((token = strtok(NULL, w_space)) == NULL) | |
487 | fatal_dump("Invalid $ipcount"); | |
488 | ipcount = atoi(token); | |
489 | for (k = 0; k < ipcount; k++) { | |
490 | if ((token = strtok(NULL, w_space)) == NULL) | |
620da955 | 491 | fatal_dump("Invalid FQDN address"); |
f88bb09c | 492 | } |
e84703ad | 493 | } else if (!strcmp(token, "$aliascount")) { |
494 | if ((token = strtok(NULL, w_space)) == NULL) | |
495 | fatal_dump("Invalid $aliascount"); | |
496 | aliascount = atoi(token); | |
497 | for (k = 0; k < aliascount; k++) { | |
498 | if ((token = strtok(NULL, w_space)) == NULL) | |
499 | fatal_dump("Invalid alias"); | |
f88bb09c | 500 | } |
e84703ad | 501 | } else if (!strcmp(token, "$ttl")) { |
502 | if ((token = strtok(NULL, w_space)) == NULL) | |
503 | fatal_dump("Invalid $ttl"); | |
504 | f.expires = squid_curtime + atoi(token); | |
f88bb09c | 505 | } else { |
e84703ad | 506 | fatal_dump("Invalid dnsserver output"); |
f88bb09c | 507 | } |
508 | } | |
e84703ad | 509 | xfree(buf); |
510 | return &f; | |
f88bb09c | 511 | } |
512 | ||
b8d8561b | 513 | static int |
514 | fqdncache_dnsHandleRead(int fd, dnsserver_t * dnsData) | |
f88bb09c | 515 | { |
f88bb09c | 516 | int len; |
517 | int svc_time; | |
518 | int n; | |
519 | fqdncache_entry *f = NULL; | |
e84703ad | 520 | fqdncache_entry *x = NULL; |
f88bb09c | 521 | |
522 | len = read(fd, | |
523 | dnsData->ip_inbuf + dnsData->offset, | |
524 | dnsData->size - dnsData->offset); | |
7cf620a9 | 525 | debug(35, 5, "fqdncache_dnsHandleRead: Result from DNS ID %d (%d bytes)\n", |
f88bb09c | 526 | dnsData->id, len); |
527 | if (len <= 0) { | |
7cf620a9 | 528 | debug(35, dnsData->flags & DNS_FLAG_CLOSING ? 5 : 1, |
f88bb09c | 529 | "FD %d: Connection from DNSSERVER #%d is closed, disabling\n", |
530 | fd, dnsData->id); | |
531 | dnsData->flags = 0; | |
532 | comm_set_select_handler(fd, | |
533 | COMM_SELECT_WRITE, | |
534 | NULL, | |
535 | NULL); | |
536 | comm_close(fd); | |
537 | return 0; | |
538 | } | |
539 | n = ++FqdncacheStats.replies; | |
b0c9fba9 | 540 | DnsStats.replies++; |
f88bb09c | 541 | dnsData->offset += len; |
542 | dnsData->ip_inbuf[dnsData->offset] = '\0'; | |
543 | ||
544 | if (strstr(dnsData->ip_inbuf, "$end\n")) { | |
545 | /* end of record found */ | |
546 | svc_time = tvSubMsec(dnsData->dispatch_time, current_time); | |
547 | if (n > FQDNCACHE_AV_FACTOR) | |
548 | n = FQDNCACHE_AV_FACTOR; | |
549 | FqdncacheStats.avg_svc_time | |
550 | = (FqdncacheStats.avg_svc_time * (n - 1) + svc_time) / n; | |
e84703ad | 551 | if ((x = fqdncache_parsebuffer(dnsData->ip_inbuf, dnsData)) == NULL) { |
552 | debug(35, 0, "fqdncache_dnsHandleRead: fqdncache_parsebuffer failed?!\n"); | |
553 | } else { | |
554 | dnsData->offset = 0; | |
555 | dnsData->ip_inbuf[0] = '\0'; | |
556 | f = dnsData->data; | |
557 | f->name_count = x->name_count; | |
7690e8eb | 558 | for (n = 0; n < (int) f->name_count; n++) |
e84703ad | 559 | f->names[n] = x->names[n]; |
560 | f->error_message = x->error_message; | |
561 | f->status = x->status; | |
562 | f->expires = x->expires; | |
563 | fqdncache_call_pending(f); | |
f88bb09c | 564 | } |
565 | } | |
566 | if (dnsData->offset == 0) { | |
567 | dnsData->data = NULL; | |
568 | dnsData->flags &= ~DNS_FLAG_BUSY; | |
569 | } | |
570 | while ((dnsData = dnsGetFirstAvailable()) && (f = fqdncacheDequeue())) | |
571 | fqdncache_dnsDispatch(dnsData, f); | |
572 | return 0; | |
573 | } | |
574 | ||
b8d8561b | 575 | static void |
576 | fqdncacheAddPending(fqdncache_entry * f, int fd, FQDNH handler, void *handlerData) | |
f88bb09c | 577 | { |
578 | struct _fqdn_pending *pending = xcalloc(1, sizeof(struct _fqdn_pending)); | |
579 | struct _fqdn_pending **I = NULL; | |
580 | ||
581 | pending->fd = fd; | |
582 | pending->handler = handler; | |
583 | pending->handlerData = handlerData; | |
584 | ||
585 | for (I = &(f->pending_head); *I; I = &((*I)->next)); | |
586 | *I = pending; | |
587 | } | |
588 | ||
b8d8561b | 589 | int |
590 | fqdncache_nbgethostbyaddr(struct in_addr addr, int fd, FQDNH handler, void *handlerData) | |
f88bb09c | 591 | { |
592 | fqdncache_entry *f = NULL; | |
593 | dnsserver_t *dnsData = NULL; | |
594 | char *name = inet_ntoa(addr); | |
595 | ||
596 | if (!handler) | |
597 | fatal_dump("fqdncache_nbgethostbyaddr: NULL handler"); | |
598 | ||
7cf620a9 | 599 | debug(35, 4, "fqdncache_nbgethostbyaddr: FD %d: Name '%s'.\n", fd, name); |
f88bb09c | 600 | FqdncacheStats.requests++; |
601 | ||
602 | if (name == NULL || name[0] == '\0') { | |
7cf620a9 | 603 | debug(35, 4, "fqdncache_nbgethostbyaddr: Invalid name!\n"); |
f88bb09c | 604 | fqdncache_call_pending_badname(fd, handler, handlerData); |
605 | return 0; | |
606 | } | |
607 | if ((f = fqdncache_get(name))) { | |
608 | if (fqdncacheExpiredEntry(f)) { | |
609 | fqdncache_release(f); | |
610 | f = NULL; | |
611 | } | |
612 | } | |
613 | if (f == NULL) { | |
614 | /* MISS: No entry, create the new one */ | |
7cf620a9 | 615 | debug(35, 5, "fqdncache_nbgethostbyaddr: MISS for '%s'\n", name); |
f88bb09c | 616 | FqdncacheStats.misses++; |
617 | f = fqdncache_create(); | |
618 | f->name = xstrdup(name); | |
619 | f->status = FQDN_PENDING; | |
620 | fqdncacheAddPending(f, fd, handler, handlerData); | |
621 | fqdncache_add_to_hash(f); | |
622 | } else if (f->status == FQDN_CACHED || f->status == FQDN_NEGATIVE_CACHED) { | |
623 | /* HIT */ | |
7cf620a9 | 624 | debug(35, 4, "fqdncache_nbgethostbyaddr: HIT for '%s'\n", name); |
f88bb09c | 625 | if (f->status == FQDN_NEGATIVE_CACHED) |
626 | FqdncacheStats.negative_hits++; | |
627 | else | |
628 | FqdncacheStats.hits++; | |
629 | fqdncacheAddPending(f, fd, handler, handlerData); | |
630 | fqdncache_call_pending(f); | |
631 | return 0; | |
632 | } else if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) { | |
7cf620a9 | 633 | debug(35, 4, "fqdncache_nbgethostbyaddr: PENDING for '%s'\n", name); |
f88bb09c | 634 | FqdncacheStats.pending_hits++; |
635 | fqdncacheAddPending(f, fd, handler, handlerData); | |
636 | return 0; | |
637 | } else { | |
638 | fatal_dump("fqdncache_nbgethostbyaddr: BAD fqdncache_entry status"); | |
639 | } | |
640 | ||
641 | /* for HIT, PENDING, DISPATCHED we've returned. For MISS we continue */ | |
642 | ||
643 | if ((dnsData = dnsGetFirstAvailable())) | |
644 | fqdncache_dnsDispatch(dnsData, f); | |
645 | else | |
646 | fqdncacheEnqueue(f); | |
647 | return 0; | |
648 | } | |
649 | ||
b8d8561b | 650 | static void |
651 | fqdncache_dnsDispatch(dnsserver_t * dns, fqdncache_entry * f) | |
f88bb09c | 652 | { |
653 | char *buf = NULL; | |
654 | if (!fqdncacheHasPending(f)) { | |
7cf620a9 | 655 | debug(35, 0, "fqdncache_dnsDispatch: skipping '%s' because no handler.\n", |
f88bb09c | 656 | f->name); |
657 | f->status = FQDN_NEGATIVE_CACHED; | |
658 | fqdncache_release(f); | |
659 | return; | |
660 | } | |
661 | f->status = FQDN_DISPATCHED; | |
662 | buf = xcalloc(1, 256); | |
663 | sprintf(buf, "%1.254s\n", f->name); | |
664 | dns->flags |= DNS_FLAG_BUSY; | |
665 | dns->data = f; | |
666 | comm_write(dns->outpipe, | |
667 | buf, | |
668 | strlen(buf), | |
669 | 0, /* timeout */ | |
670 | NULL, /* Handler */ | |
671 | NULL, /* Handler-data */ | |
672 | xfree); | |
673 | comm_set_select_handler(dns->outpipe, | |
674 | COMM_SELECT_READ, | |
675 | (PF) fqdncache_dnsHandleRead, | |
676 | dns); | |
7cf620a9 | 677 | debug(35, 5, "fqdncache_dnsDispatch: Request sent to DNS server #%d.\n", |
f88bb09c | 678 | dns->id); |
679 | dns->dispatch_time = current_time; | |
680 | DnsStats.requests++; | |
681 | DnsStats.hist[dns->id - 1]++; | |
682 | } | |
683 | ||
684 | ||
685 | /* initialize the fqdncache */ | |
b8d8561b | 686 | void |
0673c0ba | 687 | fqdncache_init(void) |
f88bb09c | 688 | { |
7cf620a9 | 689 | debug(35, 3, "Initializing FQDN Cache...\n"); |
f88bb09c | 690 | memset(&FqdncacheStats, '\0', sizeof(FqdncacheStats)); |
aabcd584 | 691 | /* small hash table */ |
692 | fqdn_table = hash_create(urlcmp, 229, hash_string); | |
f88bb09c | 693 | fqdncache_high = (long) (((float) MAX_FQDN * |
694 | (float) FQDN_HIGH_WATER) / (float) 100); | |
695 | fqdncache_low = (long) (((float) MAX_FQDN * | |
696 | (float) FQDN_LOW_WATER) / (float) 100); | |
697 | } | |
698 | ||
699 | /* clean up the pending entries in dnsserver */ | |
700 | /* return 1 if we found the host, 0 otherwise */ | |
b8d8561b | 701 | int |
702 | fqdncacheUnregister(struct in_addr addr, int fd) | |
f88bb09c | 703 | { |
03047798 | 704 | char *name = inet_ntoa(addr); |
f88bb09c | 705 | fqdncache_entry *f = NULL; |
706 | struct _fqdn_pending *p = NULL; | |
707 | int n = 0; | |
708 | ||
7cf620a9 | 709 | debug(35, 3, "fqdncache_unregister: FD %d, name '%s'\n", fd, name); |
f88bb09c | 710 | if ((f = fqdncache_get(name)) == NULL) |
711 | return 0; | |
712 | if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) { | |
713 | for (p = f->pending_head; p; p = p->next) { | |
714 | if (p->fd == fd && p->handler != NULL) { | |
715 | p->handler = NULL; | |
716 | p->fd = -1; | |
717 | n++; | |
718 | } | |
719 | } | |
720 | } | |
7cf620a9 | 721 | debug(35, 3, "fqdncache_unregister: unregistered %d handlers\n", n); |
f88bb09c | 722 | return n; |
723 | } | |
724 | ||
b8d8561b | 725 | char * |
726 | fqdncache_gethostbyaddr(struct in_addr addr, int flags) | |
f88bb09c | 727 | { |
728 | char *name = inet_ntoa(addr); | |
729 | fqdncache_entry *f = NULL; | |
730 | struct hostent *hp = NULL; | |
731 | unsigned int ip; | |
732 | ||
733 | if (!name) | |
734 | fatal_dump("fqdncache_gethostbyaddr: NULL name"); | |
735 | FqdncacheStats.requests++; | |
736 | if ((f = fqdncache_get(name))) { | |
737 | if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) { | |
738 | FqdncacheStats.pending_hits++; | |
739 | return NULL; | |
740 | } else if (f->status == FQDN_NEGATIVE_CACHED) { | |
741 | FqdncacheStats.negative_hits++; | |
742 | dns_error_message = f->error_message; | |
743 | return NULL; | |
744 | } else { | |
745 | FqdncacheStats.hits++; | |
746 | f->lastref = squid_curtime; | |
747 | return f->names[0]; | |
748 | } | |
749 | } | |
750 | FqdncacheStats.misses++; | |
751 | /* check if it's already a FQDN address in text form. */ | |
752 | if (inet_addr(name) == INADDR_NONE) { | |
753 | return name; | |
754 | } | |
755 | if (flags & FQDN_BLOCKING_LOOKUP) { | |
756 | FqdncacheStats.ghba_calls++; | |
757 | ip = inet_addr(name); | |
f7a5493b | 758 | hp = gethostbyaddr((char *) &ip, 4, AF_INET); |
f88bb09c | 759 | if (hp && hp->h_name && (hp->h_name[0] != '\0') && fqdn_table) { |
760 | /* good address, cached */ | |
761 | fqdncache_add(name, fqdncache_create(), hp, 1); | |
762 | f = fqdncache_get(name); | |
763 | return f->names[0]; | |
764 | } | |
765 | /* bad address, negative cached */ | |
766 | if (fqdn_table) | |
767 | fqdncache_add(name, fqdncache_create(), hp, 0); | |
768 | return NULL; | |
769 | } | |
770 | if (flags & FQDN_LOOKUP_IF_MISS) | |
771 | fqdncache_nbgethostbyaddr(addr, -1, dummy_handler, NULL); | |
772 | return NULL; | |
773 | } | |
774 | ||
775 | ||
776 | /* process objects list */ | |
b8d8561b | 777 | void |
778 | fqdnStats(StoreEntry * sentry) | |
f88bb09c | 779 | { |
780 | fqdncache_entry *f = NULL; | |
781 | int k; | |
782 | int ttl; | |
783 | ||
784 | if (!fqdn_table) | |
785 | return; | |
786 | ||
787 | storeAppendPrintf(sentry, "{FQDN Cache Statistics:\n"); | |
788 | storeAppendPrintf(sentry, "{FQDNcache Entries: %d}\n", | |
789 | meta_data.fqdncache_count); | |
790 | storeAppendPrintf(sentry, "{FQDNcache Requests: %d}\n", | |
791 | FqdncacheStats.requests); | |
792 | storeAppendPrintf(sentry, "{FQDNcache Hits: %d}\n", | |
793 | FqdncacheStats.hits); | |
794 | storeAppendPrintf(sentry, "{FQDNcache Pending Hits: %d}\n", | |
795 | FqdncacheStats.pending_hits); | |
796 | storeAppendPrintf(sentry, "{FQDNcache Negative Hits: %d}\n", | |
797 | FqdncacheStats.negative_hits); | |
798 | storeAppendPrintf(sentry, "{FQDNcache Misses: %d}\n", | |
799 | FqdncacheStats.misses); | |
800 | storeAppendPrintf(sentry, "{Blocking calls to gethostbyaddr(): %d}\n", | |
801 | FqdncacheStats.ghba_calls); | |
802 | storeAppendPrintf(sentry, "{dnsserver avg service time: %d msec}\n", | |
803 | FqdncacheStats.avg_svc_time); | |
804 | storeAppendPrintf(sentry, "}\n\n"); | |
805 | storeAppendPrintf(sentry, "{FQDN Cache Contents:\n\n"); | |
806 | ||
807 | for (f = fqdncache_GetFirst(); f; f = fqdncache_GetNext()) { | |
808 | if (f->status == FQDN_PENDING || f->status == FQDN_DISPATCHED) | |
809 | ttl = 0; | |
810 | else | |
e84703ad | 811 | ttl = (f->expires - squid_curtime); |
f88bb09c | 812 | storeAppendPrintf(sentry, " {%-32.32s %c %6d %d", |
813 | f->name, | |
814 | fqdncache_status_char[f->status], | |
815 | ttl, | |
816 | (int) f->name_count); | |
817 | for (k = 0; k < (int) f->name_count; k++) | |
818 | storeAppendPrintf(sentry, " %s", f->names[k]); | |
819 | storeAppendPrintf(sentry, close_bracket); | |
820 | } | |
821 | storeAppendPrintf(sentry, close_bracket); | |
822 | } | |
823 | ||
b8d8561b | 824 | static void |
825 | dummy_handler(int u1, char *u2, void *u3) | |
f88bb09c | 826 | { |
827 | return; | |
828 | } | |
829 | ||
b8d8561b | 830 | static int |
831 | fqdncacheHasPending(fqdncache_entry * f) | |
f88bb09c | 832 | { |
833 | struct _fqdn_pending *p = NULL; | |
834 | if (f->status != FQDN_PENDING) | |
835 | return 0; | |
836 | for (p = f->pending_head; p; p = p->next) | |
837 | if (p->handler) | |
838 | return 1; | |
839 | return 0; | |
840 | } | |
841 | ||
b8d8561b | 842 | void |
843 | fqdncacheReleaseInvalid(char *name) | |
f88bb09c | 844 | { |
845 | fqdncache_entry *f; | |
846 | if ((f = fqdncache_get(name)) == NULL) | |
847 | return; | |
848 | if (f->status != FQDN_NEGATIVE_CACHED) | |
849 | return; | |
850 | fqdncache_release(f); | |
851 | } | |
28ab0c0a | 852 | |
b8d8561b | 853 | char * |
854 | fqdnFromAddr(struct in_addr addr) | |
28ab0c0a | 855 | { |
856 | char *n; | |
39de381a | 857 | static char buf[32]; |
b0c9fba9 | 858 | if (Config.Log.log_fqdn && (n = fqdncache_gethostbyaddr(addr, 0))) |
28ab0c0a | 859 | return n; |
ed83f7e9 | 860 | strncpy(buf, inet_ntoa(addr), 31); |
39de381a | 861 | return buf; |
28ab0c0a | 862 | } |
f201f309 | 863 | |
b8d8561b | 864 | int |
0673c0ba | 865 | fqdncacheQueueDrain(void) |
f201f309 | 866 | { |
867 | fqdncache_entry *i; | |
868 | dnsserver_t *dnsData; | |
869 | if (!fqdncacheQueueHead) | |
a8f7d3ee | 870 | return 0; |
f201f309 | 871 | while ((dnsData = dnsGetFirstAvailable()) && (i = fqdncacheDequeue())) |
a8f7d3ee | 872 | fqdncache_dnsDispatch(dnsData, i); |
f201f309 | 873 | return 1; |
874 | } |