]>
Commit | Line | Data |
---|---|---|
f88bb09c | 1 | /* |
262a0e14 | 2 | * $Id$ |
f88bb09c | 3 | * |
7cf620a9 | 4 | * DEBUG: section 35 FQDN Cache |
f88bb09c | 5 | * AUTHOR: Harvest Derived |
6 | * | |
2b6662ba | 7 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
e25c139f | 8 | * ---------------------------------------------------------- |
f88bb09c | 9 | * |
2b6662ba | 10 | * Squid is the result of efforts by numerous individuals from |
11 | * the Internet community; see the CONTRIBUTORS file for full | |
12 | * details. Many organizations have provided support for Squid's | |
13 | * development; see the SPONSORS file for full details. Squid is | |
14 | * Copyrighted (C) 2001 by the Regents of the University of | |
15 | * California; see the COPYRIGHT file for full details. Squid | |
16 | * incorporates software developed and/or copyrighted by other | |
17 | * sources; see the CREDITS file for full details. | |
f88bb09c | 18 | * |
19 | * This program is free software; you can redistribute it and/or modify | |
20 | * it under the terms of the GNU General Public License as published by | |
21 | * the Free Software Foundation; either version 2 of the License, or | |
22 | * (at your option) any later version. | |
26ac0430 | 23 | * |
f88bb09c | 24 | * This program is distributed in the hope that it will be useful, |
25 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
26 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
27 | * GNU General Public License for more details. | |
26ac0430 | 28 | * |
f88bb09c | 29 | * You should have received a copy of the GNU General Public License |
30 | * along with this program; if not, write to the Free Software | |
cbdec147 | 31 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. |
e25c139f | 32 | * |
f88bb09c | 33 | */ |
34 | ||
35 | #include "squid.h" | |
aa839030 | 36 | #include "cbdata.h" |
a553a5a3 | 37 | #include "event.h" |
62ee09ca | 38 | #include "CacheManager.h" |
985c86bc | 39 | #include "SquidTime.h" |
e6ccf245 | 40 | #include "Store.h" |
d295d770 | 41 | #include "wordlist.h" |
f88bb09c | 42 | |
63be0a78 | 43 | /** |
44 | \defgroup FQDNCacheAPI FQDN Cache API | |
45 | \ingroup Components | |
46 | \section Introduction Introduction | |
47 | \par | |
48 | * The FQDN cache is a built-in component of squid providing | |
49 | * Hostname to IP-Number translation functionality and managing | |
50 | * the involved data-structures. Efficiency concerns require | |
51 | * mechanisms that allow non-blocking access to these mappings. | |
52 | * The FQDN cache usually doesn't block on a request except for | |
53 | * special cases where this is desired (see below). | |
54 | * | |
55 | \todo FQDN Cache should have its own API *.h file. | |
56 | */ | |
57 | ||
58 | /** | |
59 | \defgroup FQDNCacheInternal FQDN Cache Internals | |
60 | \ingroup FQDNCacheAPI | |
61 | \par | |
62 | * Internally, the execution flow is as follows: | |
63 | * On a miss, fqdncache_nbgethostbyaddr() checks whether a request | |
64 | * for this name is already pending, and if positive, it creates a | |
65 | * new entry using fqdncacheAddEntry(). Then it calls | |
66 | * fqdncacheAddPending() to add a request to the queue together | |
67 | * with data and handler. Else, ifqdncache_dnsDispatch() is called | |
68 | * to directly create a DNS query or to fqdncacheEnqueue() if all | |
69 | * no DNS port is free. | |
70 | * | |
71 | \par | |
72 | * fqdncacheCallback() is called regularly to walk down the pending | |
73 | * list and call handlers. | |
74 | * | |
75 | \par | |
76 | * LRU clean-up is performed through fqdncache_purgelru() according | |
77 | * to the fqdncache_high threshold. | |
78 | */ | |
79 | ||
80 | /// \ingroup FQDNCacheInternal | |
f88bb09c | 81 | #define FQDN_LOW_WATER 90 |
63be0a78 | 82 | |
83 | /// \ingroup FQDNCacheInternal | |
f88bb09c | 84 | #define FQDN_HIGH_WATER 95 |
f88bb09c | 85 | |
63be0a78 | 86 | /// \ingroup FQDNCacheAPI |
add5d21f | 87 | typedef struct _fqdncache_entry fqdncache_entry; |
88 | ||
63be0a78 | 89 | /** |
90 | \ingroup FQDNCacheAPI | |
91 | * The data structure used for storing name-address mappings | |
92 | * is a small hashtable (static hash_table *fqdn_table), | |
93 | * where structures of type fqdncache_entry whose most | |
94 | * interesting members are: | |
95 | */ | |
26ac0430 | 96 | struct _fqdncache_entry { |
186477c1 | 97 | hash_link hash; /* must be first */ |
add5d21f | 98 | time_t lastref; |
99 | time_t expires; | |
100 | unsigned char name_count; | |
101 | char *names[FQDN_MAX_NAMES + 1]; | |
102 | FQDNH *handler; | |
103 | void *handlerData; | |
104 | char *error_message; | |
62e76326 | 105 | |
add5d21f | 106 | struct timeval request_time; |
107 | dlink_node lru; | |
ac06f720 | 108 | unsigned short locks; |
62e76326 | 109 | |
26ac0430 | 110 | struct { |
3d0ac046 HN |
111 | unsigned int negcached:1; |
112 | unsigned int fromhosts:1; | |
2fadd50d | 113 | } flags; |
add5d21f | 114 | }; |
115 | ||
63be0a78 | 116 | /// \ingroup FQDNCacheInternal |
26ac0430 | 117 | static struct _fqdn_cache_stats { |
f88bb09c | 118 | int requests; |
119 | int replies; | |
120 | int hits; | |
121 | int misses; | |
f88bb09c | 122 | int negative_hits; |
2fadd50d | 123 | } FqdncacheStats; |
f88bb09c | 124 | |
63be0a78 | 125 | /// \ingroup FQDNCacheInternal |
4bc76d59 | 126 | static dlink_list lru_list; |
127 | ||
59f34d62 | 128 | #if USE_DNSSERVERS |
74addf6c | 129 | static HLPCB fqdncacheHandleReply; |
7ba8d0b8 | 130 | static int fqdncacheParse(fqdncache_entry *, const char *buf); |
59f34d62 | 131 | #else |
132 | static IDNSCB fqdncacheHandleReply; | |
7ba8d0b8 | 133 | static int fqdncacheParse(fqdncache_entry *, rfc1035_rr *, int, const char *error_message); |
59f34d62 | 134 | #endif |
add5d21f | 135 | static void fqdncacheRelease(fqdncache_entry *); |
136 | static fqdncache_entry *fqdncacheCreateEntry(const char *name); | |
137 | static void fqdncacheCallback(fqdncache_entry *); | |
f5b8bbc4 | 138 | static fqdncache_entry *fqdncache_get(const char *); |
348b2031 | 139 | static FQDNH dummy_handler; |
f5b8bbc4 | 140 | static int fqdncacheExpiredEntry(const fqdncache_entry *); |
f5b8bbc4 | 141 | static void fqdncacheLockEntry(fqdncache_entry * f); |
142 | static void fqdncacheUnlockEntry(fqdncache_entry * f); | |
ec878047 | 143 | static FREE fqdncacheFreeEntry; |
add5d21f | 144 | static void fqdncacheAddEntry(fqdncache_entry * f); |
f88bb09c | 145 | |
63be0a78 | 146 | /// \ingroup FQDNCacheInternal |
365e5b34 | 147 | static hash_table *fqdn_table = NULL; |
f88bb09c | 148 | |
63be0a78 | 149 | /// \ingroup FQDNCacheInternal |
24382924 | 150 | static long fqdncache_low = 180; |
63be0a78 | 151 | |
152 | /// \ingroup FQDNCacheInternal | |
24382924 | 153 | static long fqdncache_high = 200; |
f88bb09c | 154 | |
63be0a78 | 155 | /** |
156 | \ingroup FQDNCacheInternal | |
157 | * Removes the given fqdncache entry | |
158 | */ | |
b8d8561b | 159 | static void |
add5d21f | 160 | fqdncacheRelease(fqdncache_entry * f) |
f88bb09c | 161 | { |
f88bb09c | 162 | int k; |
3fda0827 | 163 | hash_remove_link(fqdn_table, (hash_link *) f); |
62e76326 | 164 | |
add5d21f | 165 | for (k = 0; k < (int) f->name_count; k++) |
62e76326 | 166 | safe_free(f->names[k]); |
167 | ||
bf8fe701 | 168 | debugs(35, 5, "fqdncacheRelease: Released FQDN record for '" << hashKeyStr(&f->hash) << "'."); |
62e76326 | 169 | |
4bc76d59 | 170 | dlinkDelete(&f->lru, &lru_list); |
62e76326 | 171 | |
186477c1 | 172 | safe_free(f->hash.key); |
62e76326 | 173 | |
429fdbec | 174 | safe_free(f->error_message); |
62e76326 | 175 | |
db1cd23c | 176 | memFree(f, MEM_FQDNCACHE_ENTRY); |
f88bb09c | 177 | } |
178 | ||
63be0a78 | 179 | /** |
180 | \ingroup FQDNCacheInternal | |
181 | \param name FQDN hash string. | |
182 | \retval Match for given name | |
183 | */ | |
b8d8561b | 184 | static fqdncache_entry * |
0ee4272b | 185 | fqdncache_get(const char *name) |
f88bb09c | 186 | { |
187 | hash_link *e; | |
188 | static fqdncache_entry *f; | |
f88bb09c | 189 | f = NULL; |
62e76326 | 190 | |
f88bb09c | 191 | if (fqdn_table) { |
62e76326 | 192 | if ((e = (hash_link *)hash_lookup(fqdn_table, name)) != NULL) |
193 | f = (fqdncache_entry *) e; | |
f88bb09c | 194 | } |
62e76326 | 195 | |
f88bb09c | 196 | return f; |
197 | } | |
198 | ||
63be0a78 | 199 | /// \ingroup FQDNCacheInternal |
b8d8561b | 200 | static int |
fe4e214f | 201 | fqdncacheExpiredEntry(const fqdncache_entry * f) |
f88bb09c | 202 | { |
0e70aa1e | 203 | /* all static entries are locked, so this takes care of them too */ |
62e76326 | 204 | |
429fdbec | 205 | if (f->locks != 0) |
62e76326 | 206 | return 0; |
207 | ||
e84703ad | 208 | if (f->expires > squid_curtime) |
62e76326 | 209 | return 0; |
210 | ||
f88bb09c | 211 | return 1; |
212 | } | |
213 | ||
63be0a78 | 214 | /// \ingroup FQDNCacheAPI |
59c4d35b | 215 | void |
216 | fqdncache_purgelru(void *notused) | |
f88bb09c | 217 | { |
4bc76d59 | 218 | dlink_node *m; |
219 | dlink_node *prev = NULL; | |
220 | fqdncache_entry *f; | |
f88bb09c | 221 | int removed = 0; |
52040193 | 222 | eventAdd("fqdncache_purgelru", fqdncache_purgelru, NULL, 10.0, 1); |
62e76326 | 223 | |
4bc76d59 | 224 | for (m = lru_list.tail; m; m = prev) { |
62e76326 | 225 | if (memInUse(MEM_FQDNCACHE_ENTRY) < fqdncache_low) |
226 | break; | |
227 | ||
228 | prev = m->prev; | |
229 | ||
230 | f = (fqdncache_entry *)m->data; | |
231 | ||
232 | if (f->locks != 0) | |
233 | continue; | |
234 | ||
235 | fqdncacheRelease(f); | |
236 | ||
237 | removed++; | |
f88bb09c | 238 | } |
62e76326 | 239 | |
bf8fe701 | 240 | debugs(35, 9, "fqdncache_purgelru: removed " << removed << " entries"); |
f88bb09c | 241 | } |
242 | ||
63be0a78 | 243 | /// \ingroup FQDNCacheAPI |
0e70aa1e | 244 | static void |
245 | purge_entries_fromhosts(void) | |
246 | { | |
247 | dlink_node *m = lru_list.head; | |
248 | fqdncache_entry *i = NULL; | |
249 | fqdncache_entry *t; | |
62e76326 | 250 | |
0e70aa1e | 251 | while (m) { |
62e76326 | 252 | if (i != NULL) { /* need to delay deletion */ |
253 | fqdncacheRelease(i); /* we just override locks */ | |
254 | i = NULL; | |
255 | } | |
256 | ||
257 | t = (fqdncache_entry *)m->data; | |
258 | ||
259 | if (t->flags.fromhosts) | |
260 | i = t; | |
261 | ||
262 | m = m->next; | |
0e70aa1e | 263 | } |
62e76326 | 264 | |
0e70aa1e | 265 | if (i != NULL) |
62e76326 | 266 | fqdncacheRelease(i); |
0e70aa1e | 267 | } |
268 | ||
63be0a78 | 269 | /** |
270 | \ingroup FQDNCacheInternal | |
271 | * | |
272 | * Create blank fqdncache_entry | |
273 | */ | |
b8d8561b | 274 | static fqdncache_entry * |
add5d21f | 275 | fqdncacheCreateEntry(const char *name) |
f88bb09c | 276 | { |
4bc76d59 | 277 | static fqdncache_entry *f; |
e6ccf245 | 278 | f = (fqdncache_entry *)memAllocate(MEM_FQDNCACHE_ENTRY); |
186477c1 | 279 | f->hash.key = xstrdup(name); |
4bc76d59 | 280 | f->expires = squid_curtime + Config.negativeDnsTtl; |
4bc76d59 | 281 | return f; |
f88bb09c | 282 | } |
283 | ||
63be0a78 | 284 | /// \ingroup FQDNCacheInternal |
b8d8561b | 285 | static void |
add5d21f | 286 | fqdncacheAddEntry(fqdncache_entry * f) |
f88bb09c | 287 | { |
e6ccf245 | 288 | hash_link *e = (hash_link *)hash_lookup(fqdn_table, f->hash.key); |
62e76326 | 289 | |
add5d21f | 290 | if (NULL != e) { |
62e76326 | 291 | /* avoid colission */ |
292 | fqdncache_entry *q = (fqdncache_entry *) e; | |
293 | fqdncacheRelease(q); | |
429fdbec | 294 | } |
62e76326 | 295 | |
186477c1 | 296 | hash_join(fqdn_table, &f->hash); |
add5d21f | 297 | dlinkAdd(f, &f->lru, &lru_list); |
429fdbec | 298 | f->lastref = squid_curtime; |
f88bb09c | 299 | } |
300 | ||
63be0a78 | 301 | /** |
302 | \ingroup FQDNCacheInternal | |
303 | * | |
304 | * Walks down the pending list, calling handlers | |
305 | */ | |
b8d8561b | 306 | static void |
add5d21f | 307 | fqdncacheCallback(fqdncache_entry * f) |
f88bb09c | 308 | { |
fa80a8ef | 309 | FQDNH *callback; |
310 | void *cbdata; | |
f88bb09c | 311 | f->lastref = squid_curtime; |
62e76326 | 312 | |
fa80a8ef | 313 | if (!f->handler) |
62e76326 | 314 | return; |
315 | ||
5a2aa048 | 316 | fqdncacheLockEntry(f); |
62e76326 | 317 | |
fa80a8ef | 318 | callback = f->handler; |
62e76326 | 319 | |
add5d21f | 320 | f->handler = NULL; |
62e76326 | 321 | |
fa80a8ef | 322 | if (cbdataReferenceValidDone(f->handlerData, &cbdata)) { |
62e76326 | 323 | dns_error_message = f->error_message; |
d6d3dbd4 | 324 | callback(f->name_count ? f->names[0] : NULL, cbdata); |
f88bb09c | 325 | } |
62e76326 | 326 | |
429fdbec | 327 | fqdncacheUnlockEntry(f); |
f88bb09c | 328 | } |
329 | ||
63be0a78 | 330 | /// \ingroup FQDNCacheInternal |
b18baa48 | 331 | #if USE_DNSSERVERS |
7ba8d0b8 | 332 | static int |
333 | fqdncacheParse(fqdncache_entry *f, const char *inbuf) | |
f88bb09c | 334 | { |
bd34f258 | 335 | LOCAL_ARRAY(char, buf, DNS_INBUF_SZ); |
e84703ad | 336 | char *token; |
bd34f258 | 337 | int ttl; |
7ba8d0b8 | 338 | const char *name = (const char *)f->hash.key; |
339 | f->expires = squid_curtime + Config.negativeDnsTtl; | |
340 | f->flags.negcached = 1; | |
62e76326 | 341 | |
c68e9c6b | 342 | if (inbuf == NULL) { |
bf8fe701 | 343 | debugs(35, 1, "fqdncacheParse: Got <NULL> reply in response to '" << name << "'"); |
7ba8d0b8 | 344 | f->error_message = xstrdup("Internal Error"); |
345 | return -1; | |
c68e9c6b | 346 | } |
62e76326 | 347 | |
c579f632 | 348 | xstrncpy(buf, inbuf, DNS_INBUF_SZ); |
bf8fe701 | 349 | debugs(35, 5, "fqdncacheParse: parsing: {" << buf << "}"); |
bd34f258 | 350 | token = strtok(buf, w_space); |
62e76326 | 351 | |
bd34f258 | 352 | if (NULL == token) { |
bf8fe701 | 353 | debugs(35, 1, "fqdncacheParse: Got <NULL>, expecting '$name' in response to '" << name << "'"); |
7ba8d0b8 | 354 | f->error_message = xstrdup("Internal Error"); |
355 | return -1; | |
bd34f258 | 356 | } |
62e76326 | 357 | |
bd34f258 | 358 | if (0 == strcmp(token, "$fail")) { |
62e76326 | 359 | token = strtok(NULL, "\n"); |
360 | assert(NULL != token); | |
7ba8d0b8 | 361 | f->error_message = xstrdup(token); |
362 | return 0; | |
bd34f258 | 363 | } |
62e76326 | 364 | |
bd34f258 | 365 | if (0 != strcmp(token, "$name")) { |
bf8fe701 | 366 | debugs(35, 1, "fqdncacheParse: Got '" << inbuf << "', expecting '$name' in response to '" << name << "'"); |
7ba8d0b8 | 367 | f->error_message = xstrdup("Internal Error"); |
368 | return -1; | |
bd34f258 | 369 | } |
62e76326 | 370 | |
bd34f258 | 371 | token = strtok(NULL, w_space); |
62e76326 | 372 | |
bd34f258 | 373 | if (NULL == token) { |
bf8fe701 | 374 | debugs(35, 1, "fqdncacheParse: Got '" << inbuf << "', expecting TTL in response to '" << name << "'"); |
7ba8d0b8 | 375 | f->error_message = xstrdup("Internal Error"); |
376 | return -1; | |
bd34f258 | 377 | } |
62e76326 | 378 | |
bd34f258 | 379 | ttl = atoi(token); |
62e76326 | 380 | |
bd34f258 | 381 | token = strtok(NULL, w_space); |
62e76326 | 382 | |
7ba8d0b8 | 383 | if (NULL == token) { |
bf8fe701 | 384 | debugs(35, 1, "fqdncacheParse: Got '" << inbuf << "', expecting hostname in response to '" << name << "'"); |
7ba8d0b8 | 385 | f->error_message = xstrdup("Internal Error"); |
386 | return -1; | |
f88bb09c | 387 | } |
62e76326 | 388 | |
7ba8d0b8 | 389 | f->names[0] = xstrdup(token); |
390 | f->name_count = 1; | |
391 | ||
392 | if (ttl == 0 || ttl > Config.positiveDnsTtl) | |
393 | ttl = Config.positiveDnsTtl; | |
394 | ||
395 | if (ttl < Config.negativeDnsTtl) | |
396 | ttl = Config.negativeDnsTtl; | |
397 | ||
398 | f->expires = squid_curtime + ttl; | |
399 | ||
400 | f->flags.negcached = 0; | |
401 | ||
402 | return f->name_count; | |
f88bb09c | 403 | } |
62e76326 | 404 | |
59f34d62 | 405 | #else |
7ba8d0b8 | 406 | static int |
407 | fqdncacheParse(fqdncache_entry *f, rfc1035_rr * answers, int nr, const char *error_message) | |
59f34d62 | 408 | { |
59f34d62 | 409 | int k; |
7ba8d0b8 | 410 | int ttl = 0; |
411 | const char *name = (const char *)f->hash.key; | |
412 | f->expires = squid_curtime + Config.negativeDnsTtl; | |
413 | f->flags.negcached = 1; | |
62e76326 | 414 | |
59f34d62 | 415 | if (nr < 0) { |
bf8fe701 | 416 | debugs(35, 3, "fqdncacheParse: Lookup of '" << name << "' failed (" << error_message << ")"); |
7ba8d0b8 | 417 | f->error_message = xstrdup(error_message); |
418 | return -1; | |
59f34d62 | 419 | } |
62e76326 | 420 | |
59f34d62 | 421 | if (nr == 0) { |
bf8fe701 | 422 | debugs(35, 3, "fqdncacheParse: No DNS records for '" << name << "'"); |
7ba8d0b8 | 423 | f->error_message = xstrdup("No DNS records"); |
424 | return 0; | |
59f34d62 | 425 | } |
62e76326 | 426 | |
bf8fe701 | 427 | debugs(35, 3, "fqdncacheParse: " << nr << " answers for '" << name << "'"); |
59f34d62 | 428 | assert(answers); |
62e76326 | 429 | |
1810dde6 | 430 | for (k = 0; k < nr; k++) { |
62e76326 | 431 | if (answers[k]._class != RFC1035_CLASS_IN) |
432 | continue; | |
433 | ||
2d320a3a | 434 | if (answers[k].type == RFC1035_TYPE_PTR) { |
435 | if (!answers[k].rdata[0]) { | |
bf8fe701 | 436 | debugs(35, 2, "fqdncacheParse: blank PTR record for '" << name << "'"); |
2d320a3a | 437 | continue; |
438 | } | |
470cd749 | 439 | |
2d320a3a | 440 | if (strchr(answers[k].rdata, ' ')) { |
bf8fe701 | 441 | debugs(35, 2, "fqdncacheParse: invalid PTR record '" << answers[k].rdata << "' for '" << name << "'"); |
2d320a3a | 442 | continue; |
443 | } | |
444 | ||
445 | f->names[f->name_count++] = xstrdup(answers[k].rdata); | |
446 | } else if (answers[k].type != RFC1035_TYPE_CNAME) | |
447 | continue; | |
62e76326 | 448 | |
7ba8d0b8 | 449 | if (ttl == 0 || (int) answers[k].ttl < ttl) |
450 | ttl = answers[k].ttl; | |
62e76326 | 451 | |
7ba8d0b8 | 452 | if (f->name_count >= FQDN_MAX_NAMES) |
453 | break; | |
454 | } | |
62e76326 | 455 | |
7ba8d0b8 | 456 | if (f->name_count == 0) { |
bf8fe701 | 457 | debugs(35, 1, "fqdncacheParse: No PTR record for '" << name << "'"); |
7ba8d0b8 | 458 | return 0; |
459 | } | |
62e76326 | 460 | |
3e8c4107 | 461 | if (ttl > Config.positiveDnsTtl) |
7ba8d0b8 | 462 | ttl = Config.positiveDnsTtl; |
62e76326 | 463 | |
7ba8d0b8 | 464 | if (ttl < Config.negativeDnsTtl) |
465 | ttl = Config.negativeDnsTtl; | |
466 | ||
467 | f->expires = squid_curtime + ttl; | |
62e76326 | 468 | |
7ba8d0b8 | 469 | f->flags.negcached = 0; |
470 | ||
471 | return f->name_count; | |
59f34d62 | 472 | } |
62e76326 | 473 | |
59f34d62 | 474 | #endif |
f88bb09c | 475 | |
aa839030 | 476 | |
63be0a78 | 477 | /** |
478 | \ingroup FQDNCacheAPI | |
479 | * | |
480 | * Callback for handling DNS results. | |
481 | */ | |
429fdbec | 482 | static void |
59f34d62 | 483 | #if USE_DNSSERVERS |
74addf6c | 484 | fqdncacheHandleReply(void *data, char *reply) |
59f34d62 | 485 | #else |
7ba8d0b8 | 486 | fqdncacheHandleReply(void *data, rfc1035_rr * answers, int na, const char *error_message) |
59f34d62 | 487 | #endif |
f88bb09c | 488 | { |
f88bb09c | 489 | int n; |
aa839030 | 490 | fqdncache_entry *f; |
491 | static_cast<generic_cbdata *>(data)->unwrap(&f); | |
c68e9c6b | 492 | n = ++FqdncacheStats.replies; |
83704487 | 493 | statHistCount(&statCounter.dns.svc_time, |
62e76326 | 494 | tvSubMsec(f->request_time, current_time)); |
59f34d62 | 495 | #if USE_DNSSERVERS |
62e76326 | 496 | |
7ba8d0b8 | 497 | fqdncacheParse(f, reply); |
59f34d62 | 498 | #else |
62e76326 | 499 | |
7ba8d0b8 | 500 | fqdncacheParse(f, answers, na, error_message); |
59f34d62 | 501 | #endif |
62e76326 | 502 | |
add5d21f | 503 | fqdncacheAddEntry(f); |
62e76326 | 504 | |
add5d21f | 505 | fqdncacheCallback(f); |
f88bb09c | 506 | } |
507 | ||
63be0a78 | 508 | /** |
509 | \ingroup FQDNCacheAPI | |
510 | * | |
511 | \param addr IP address of domain to resolve. | |
512 | \param handler A pointer to the function to be called when | |
513 | * the reply from the FQDN cache | |
514 | * (or the DNS if the FQDN cache misses) | |
515 | \param handlerData Information that is passed to the handler | |
516 | * and does not affect the FQDN cache. | |
517 | */ | |
429fdbec | 518 | void |
ad61a2b4 | 519 | fqdncache_nbgethostbyaddr(IpAddress &addr, FQDNH * handler, void *handlerData) |
f88bb09c | 520 | { |
521 | fqdncache_entry *f = NULL; | |
cc192b50 | 522 | char name[MAX_IPSTRLEN]; |
74addf6c | 523 | generic_cbdata *c; |
698e6f16 | 524 | assert(handler); |
cc192b50 | 525 | addr.NtoA(name,MAX_IPSTRLEN); |
bf8fe701 | 526 | debugs(35, 4, "fqdncache_nbgethostbyaddr: Name '" << name << "'."); |
f88bb09c | 527 | FqdncacheStats.requests++; |
62e76326 | 528 | |
26ac0430 | 529 | if (name[0] == '\0') { |
bf8fe701 | 530 | debugs(35, 4, "fqdncache_nbgethostbyaddr: Invalid name!"); |
2ffff82e | 531 | dns_error_message = "Invalid hostname"; |
62e76326 | 532 | handler(NULL, handlerData); |
533 | return; | |
f88bb09c | 534 | } |
62e76326 | 535 | |
add5d21f | 536 | f = fqdncache_get(name); |
62e76326 | 537 | |
26ac0430 | 538 | if (NULL == f) { |
62e76326 | 539 | /* miss */ |
540 | (void) 0; | |
26ac0430 | 541 | } else if (fqdncacheExpiredEntry(f)) { |
62e76326 | 542 | /* hit, but expired -- bummer */ |
543 | fqdncacheRelease(f); | |
544 | f = NULL; | |
26ac0430 | 545 | } else { |
62e76326 | 546 | /* hit */ |
bf8fe701 | 547 | debugs(35, 4, "fqdncache_nbgethostbyaddr: HIT for '" << name << "'"); |
62e76326 | 548 | |
549 | if (f->flags.negcached) | |
550 | FqdncacheStats.negative_hits++; | |
551 | else | |
552 | FqdncacheStats.hits++; | |
553 | ||
554 | f->handler = handler; | |
555 | ||
556 | f->handlerData = cbdataReference(handlerData); | |
557 | ||
558 | fqdncacheCallback(f); | |
559 | ||
560 | return; | |
f88bb09c | 561 | } |
add5d21f | 562 | |
bf8fe701 | 563 | debugs(35, 5, "fqdncache_nbgethostbyaddr: MISS for '" << name << "'"); |
add5d21f | 564 | FqdncacheStats.misses++; |
565 | f = fqdncacheCreateEntry(name); | |
566 | f->handler = handler; | |
fa80a8ef | 567 | f->handlerData = cbdataReference(handlerData); |
add5d21f | 568 | f->request_time = current_time; |
aa839030 | 569 | c = new generic_cbdata(f); |
59f34d62 | 570 | #if USE_DNSSERVERS |
62e76326 | 571 | |
186477c1 | 572 | dnsSubmit(hashKeyStr(&f->hash), fqdncacheHandleReply, c); |
59f34d62 | 573 | #else |
574 | idnsPTRLookup(addr, fqdncacheHandleReply, c); | |
575 | #endif | |
f88bb09c | 576 | } |
577 | ||
5f5e883f FC |
578 | /// \ingroup FQDNCacheInternal |
579 | static void | |
580 | fqdncacheRegisterWithCacheManager(void) | |
581 | { | |
582 | CacheManager::GetInstance()-> | |
26ac0430 AJ |
583 | registerAction("fqdncache", "FQDN Cache Stats and Contents", |
584 | fqdnStats, 0, 1); | |
5f5e883f FC |
585 | |
586 | } | |
587 | ||
63be0a78 | 588 | /** |
589 | \ingroup FQDNCacheAPI | |
590 | * | |
591 | * Initialize the fqdncache. | |
592 | * Called after IP cache initialization. | |
593 | */ | |
b8d8561b | 594 | void |
0673c0ba | 595 | fqdncache_init(void) |
f88bb09c | 596 | { |
aa9e2cab | 597 | int n; |
62e76326 | 598 | |
ea391f18 FC |
599 | fqdncacheRegisterWithCacheManager(); |
600 | ||
19054954 | 601 | if (fqdn_table) |
62e76326 | 602 | return; |
603 | ||
bf8fe701 | 604 | debugs(35, 3, "Initializing FQDN Cache..."); |
62e76326 | 605 | |
f88bb09c | 606 | memset(&FqdncacheStats, '\0', sizeof(FqdncacheStats)); |
62e76326 | 607 | |
3eb55834 | 608 | memset(&lru_list, '\0', sizeof(lru_list)); |
62e76326 | 609 | |
e55650e3 | 610 | fqdncache_high = (long) (((float) Config.fqdncache.size * |
62e76326 | 611 | (float) FQDN_HIGH_WATER) / (float) 100); |
612 | ||
e55650e3 | 613 | fqdncache_low = (long) (((float) Config.fqdncache.size * |
62e76326 | 614 | (float) FQDN_LOW_WATER) / (float) 100); |
615 | ||
aa9e2cab | 616 | n = hashPrime(fqdncache_high / 4); |
62e76326 | 617 | |
30abd221 | 618 | fqdn_table = hash_create((HASHCMP *) strcmp, n, hash4); |
62e76326 | 619 | |
add5d21f | 620 | memDataInit(MEM_FQDNCACHE_ENTRY, "fqdncache_entry", |
62e76326 | 621 | sizeof(fqdncache_entry), 0); |
f88bb09c | 622 | } |
623 | ||
63be0a78 | 624 | /** |
625 | \ingroup FQDNCacheAPI | |
626 | * | |
627 | * Is different in that it only checks if an entry exists in | |
628 | * it's data-structures and does not by default contact the | |
629 | * DNS, unless this is requested, by setting the flags | |
630 | * to FQDN_LOOKUP_IF_MISS. | |
631 | * | |
632 | \param addr address of the FQDN being resolved | |
633 | \param flags values are NULL or FQDN_LOOKUP_IF_MISS. default is NULL. | |
634 | * | |
635 | */ | |
0ee4272b | 636 | const char * |
ad61a2b4 | 637 | fqdncache_gethostbyaddr(IpAddress &addr, int flags) |
f88bb09c | 638 | { |
cc192b50 | 639 | char name[MAX_IPSTRLEN]; |
f88bb09c | 640 | fqdncache_entry *f = NULL; |
62e76326 | 641 | |
cc192b50 | 642 | assert(!addr.IsAnyAddr() && !addr.IsNoAddr()); |
643 | addr.NtoA(name,MAX_IPSTRLEN); | |
f88bb09c | 644 | FqdncacheStats.requests++; |
add5d21f | 645 | f = fqdncache_get(name); |
62e76326 | 646 | |
26ac0430 | 647 | if (NULL == f) { |
62e76326 | 648 | (void) 0; |
26ac0430 | 649 | } else if (fqdncacheExpiredEntry(f)) { |
62e76326 | 650 | fqdncacheRelease(f); |
651 | f = NULL; | |
26ac0430 | 652 | } else if (f->flags.negcached) { |
62e76326 | 653 | FqdncacheStats.negative_hits++; |
654 | dns_error_message = f->error_message; | |
655 | return NULL; | |
26ac0430 | 656 | } else { |
62e76326 | 657 | FqdncacheStats.hits++; |
658 | f->lastref = squid_curtime; | |
2ffff82e | 659 | dns_error_message = f->error_message; |
62e76326 | 660 | return f->names[0]; |
f88bb09c | 661 | } |
62e76326 | 662 | |
2ffff82e | 663 | dns_error_message = NULL; |
664 | ||
429fdbec | 665 | FqdncacheStats.misses++; |
62e76326 | 666 | |
26ac0430 | 667 | if (flags & FQDN_LOOKUP_IF_MISS) { |
62e76326 | 668 | fqdncache_nbgethostbyaddr(addr, dummy_handler, NULL); |
cc192b50 | 669 | } |
62e76326 | 670 | |
f88bb09c | 671 | return NULL; |
672 | } | |
673 | ||
674 | ||
63be0a78 | 675 | /** |
676 | \ingroup FQDNCacheInternal | |
677 | * | |
678 | * Process objects list | |
679 | */ | |
b8d8561b | 680 | void |
681 | fqdnStats(StoreEntry * sentry) | |
f88bb09c | 682 | { |
683 | fqdncache_entry *f = NULL; | |
684 | int k; | |
685 | int ttl; | |
62e76326 | 686 | |
4bc76d59 | 687 | if (fqdn_table == NULL) |
62e76326 | 688 | return; |
689 | ||
15576b6a | 690 | storeAppendPrintf(sentry, "FQDN Cache Statistics:\n"); |
62e76326 | 691 | |
15576b6a | 692 | storeAppendPrintf(sentry, "FQDNcache Entries: %d\n", |
62e76326 | 693 | memInUse(MEM_FQDNCACHE_ENTRY)); |
694 | ||
15576b6a | 695 | storeAppendPrintf(sentry, "FQDNcache Requests: %d\n", |
62e76326 | 696 | FqdncacheStats.requests); |
697 | ||
15576b6a | 698 | storeAppendPrintf(sentry, "FQDNcache Hits: %d\n", |
62e76326 | 699 | FqdncacheStats.hits); |
700 | ||
15576b6a | 701 | storeAppendPrintf(sentry, "FQDNcache Negative Hits: %d\n", |
62e76326 | 702 | FqdncacheStats.negative_hits); |
703 | ||
15576b6a | 704 | storeAppendPrintf(sentry, "FQDNcache Misses: %d\n", |
62e76326 | 705 | FqdncacheStats.misses); |
706 | ||
15576b6a | 707 | storeAppendPrintf(sentry, "FQDN Cache Contents:\n\n"); |
62e76326 | 708 | |
cc192b50 | 709 | storeAppendPrintf(sentry, "%-45.45s %3s %3s %3s %s\n", |
62e76326 | 710 | "Address", "Flg", "TTL", "Cnt", "Hostnames"); |
711 | ||
0f6bebac | 712 | hash_first(fqdn_table); |
62e76326 | 713 | |
0f6bebac | 714 | while ((f = (fqdncache_entry *) hash_next(fqdn_table))) { |
62e76326 | 715 | ttl = (f->flags.fromhosts ? -1 : (f->expires - squid_curtime)); |
cc192b50 | 716 | storeAppendPrintf(sentry, "%-45.45s %c%c %3.3d % 3d", |
62e76326 | 717 | hashKeyStr(&f->hash), |
718 | f->flags.negcached ? 'N' : ' ', | |
719 | f->flags.fromhosts ? 'H' : ' ', | |
720 | ttl, | |
721 | (int) f->name_count); | |
722 | ||
723 | for (k = 0; k < (int) f->name_count; k++) | |
724 | storeAppendPrintf(sentry, " %s", f->names[k]); | |
725 | ||
726 | storeAppendPrintf(sentry, "\n"); | |
f88bb09c | 727 | } |
f88bb09c | 728 | } |
729 | ||
63be0a78 | 730 | /// \ingroup FQDNCacheInternal |
b8d8561b | 731 | static void |
79d39a72 | 732 | dummy_handler(const char *bufnotused, void *datanotused) |
f88bb09c | 733 | { |
734 | return; | |
735 | } | |
736 | ||
63be0a78 | 737 | /// \ingroup FQDNCacheAPI |
0ee4272b | 738 | const char * |
ad61a2b4 | 739 | fqdnFromAddr(IpAddress &addr) |
28ab0c0a | 740 | { |
0ee4272b | 741 | const char *n; |
cc192b50 | 742 | static char buf[MAX_IPSTRLEN]; |
62e76326 | 743 | |
17a0a4ee | 744 | if (Config.onoff.log_fqdn && (n = fqdncache_gethostbyaddr(addr, 0))) |
62e76326 | 745 | return n; |
746 | ||
63be0a78 | 747 | /// \todo Perhapse this should use toHostname() instead of straight NtoA. |
5ad35449 | 748 | /// that would wrap the IPv6 properly when raw. |
cc192b50 | 749 | addr.NtoA(buf, MAX_IPSTRLEN); |
62e76326 | 750 | |
39de381a | 751 | return buf; |
28ab0c0a | 752 | } |
f201f309 | 753 | |
63be0a78 | 754 | /// \ingroup FQDNCacheInternal |
429fdbec | 755 | static void |
756 | fqdncacheLockEntry(fqdncache_entry * f) | |
757 | { | |
4bc76d59 | 758 | if (f->locks++ == 0) { |
62e76326 | 759 | dlinkDelete(&f->lru, &lru_list); |
760 | dlinkAdd(f, &f->lru, &lru_list); | |
4bc76d59 | 761 | } |
429fdbec | 762 | } |
763 | ||
63be0a78 | 764 | /// \ingroup FQDNCacheInternal |
429fdbec | 765 | static void |
766 | fqdncacheUnlockEntry(fqdncache_entry * f) | |
767 | { | |
ac06f720 | 768 | assert(f->locks > 0); |
429fdbec | 769 | f->locks--; |
62e76326 | 770 | |
429fdbec | 771 | if (fqdncacheExpiredEntry(f)) |
62e76326 | 772 | fqdncacheRelease(f); |
429fdbec | 773 | } |
774 | ||
63be0a78 | 775 | /// \ingroup FQDNCacheInternal |
ec878047 | 776 | static void |
777 | fqdncacheFreeEntry(void *data) | |
778 | { | |
e6ccf245 | 779 | fqdncache_entry *f = (fqdncache_entry *)data; |
ec878047 | 780 | int k; |
62e76326 | 781 | |
ec878047 | 782 | for (k = 0; k < (int) f->name_count; k++) |
62e76326 | 783 | safe_free(f->names[k]); |
784 | ||
186477c1 | 785 | safe_free(f->hash.key); |
62e76326 | 786 | |
ec878047 | 787 | safe_free(f->error_message); |
62e76326 | 788 | |
db1cd23c | 789 | memFree(f, MEM_FQDNCACHE_ENTRY); |
ec878047 | 790 | } |
791 | ||
63be0a78 | 792 | /// \ingroup FQDNCacheAPI |
56e15c50 | 793 | void |
794 | fqdncacheFreeMemory(void) | |
795 | { | |
ec878047 | 796 | hashFreeItems(fqdn_table, fqdncacheFreeEntry); |
56e15c50 | 797 | hashFreeMemory(fqdn_table); |
afe95a7e | 798 | fqdn_table = NULL; |
56e15c50 | 799 | } |
429fdbec | 800 | |
63be0a78 | 801 | /** |
802 | \ingroup FQDNCacheAPI | |
803 | * | |
804 | * Recalculate FQDN cache size upon reconfigure. | |
805 | * Is called to clear the FQDN cache's data structures, | |
806 | * cancel all pending requests. | |
807 | */ | |
429fdbec | 808 | void |
809 | fqdncache_restart(void) | |
810 | { | |
e55650e3 | 811 | fqdncache_high = (long) (((float) Config.fqdncache.size * |
62e76326 | 812 | (float) FQDN_HIGH_WATER) / (float) 100); |
e55650e3 | 813 | fqdncache_low = (long) (((float) Config.fqdncache.size * |
62e76326 | 814 | (float) FQDN_LOW_WATER) / (float) 100); |
0e70aa1e | 815 | purge_entries_fromhosts(); |
816 | } | |
817 | ||
63be0a78 | 818 | /** |
819 | \ingroup FQDNCacheAPI | |
820 | * | |
821 | * Adds a "static" entry from /etc/hosts. | |
822 | \par | |
823 | * The worldist is to be managed by the caller, | |
824 | * including pointed-to strings | |
825 | * | |
826 | \param addr FQDN name to be added. | |
827 | \param hostnames ?? | |
0e70aa1e | 828 | */ |
829 | void | |
830 | fqdncacheAddEntryFromHosts(char *addr, wordlist * hostnames) | |
831 | { | |
832 | fqdncache_entry *fce; | |
833 | int j = 0; | |
62e76326 | 834 | |
0e70aa1e | 835 | if ((fce = fqdncache_get(addr))) { |
62e76326 | 836 | if (1 == fce->flags.fromhosts) { |
837 | fqdncacheUnlockEntry(fce); | |
838 | } else if (fce->locks > 0) { | |
bf8fe701 | 839 | debugs(35, 1, "fqdncacheAddEntryFromHosts: can't add static entry for locked address '" << addr << "'"); |
62e76326 | 840 | return; |
841 | } else { | |
842 | fqdncacheRelease(fce); | |
843 | } | |
0e70aa1e | 844 | } |
62e76326 | 845 | |
0e70aa1e | 846 | fce = fqdncacheCreateEntry(addr); |
62e76326 | 847 | |
0e70aa1e | 848 | while (hostnames) { |
62e76326 | 849 | fce->names[j] = xstrdup(hostnames->key); |
850 | j++; | |
851 | hostnames = hostnames->next; | |
852 | ||
853 | if (j >= FQDN_MAX_NAMES) | |
854 | break; | |
0e70aa1e | 855 | } |
62e76326 | 856 | |
0e70aa1e | 857 | fce->name_count = j; |
858 | fce->names[j] = NULL; /* it's safe */ | |
859 | fce->flags.fromhosts = 1; | |
860 | fqdncacheAddEntry(fce); | |
861 | fqdncacheLockEntry(fce); | |
429fdbec | 862 | } |
ce75f381 | 863 | |
0e70aa1e | 864 | |
ce75f381 | 865 | #ifdef SQUID_SNMP |
63be0a78 | 866 | /** |
867 | * \ingroup FQDNCacheAPI | |
868 | * The function to return the FQDN statistics via SNMP | |
135171fe | 869 | */ |
86115da5 | 870 | variable_list * |
e7ef99a7 | 871 | snmp_netFqdnFn(variable_list * Var, snint * ErrP) |
d60c11be | 872 | { |
736eb6ad | 873 | variable_list *Answer = NULL; |
bf8fe701 | 874 | debugs(49, 5, "snmp_netFqdnFn: Processing request:"); |
e7ef99a7 | 875 | snmpDebugOid(5, Var->name, Var->name_length); |
86115da5 | 876 | *ErrP = SNMP_ERR_NOERROR; |
62e76326 | 877 | |
135171fe | 878 | switch (Var->name[LEN_SQ_NET + 1]) { |
62e76326 | 879 | |
e7ef99a7 | 880 | case FQDN_ENT: |
62e76326 | 881 | Answer = snmp_var_new_integer(Var->name, Var->name_length, |
882 | memInUse(MEM_FQDNCACHE_ENTRY), | |
883 | SMI_GAUGE32); | |
884 | break; | |
885 | ||
e7ef99a7 | 886 | case FQDN_REQ: |
62e76326 | 887 | Answer = snmp_var_new_integer(Var->name, Var->name_length, |
888 | FqdncacheStats.requests, | |
889 | SMI_COUNTER32); | |
890 | break; | |
891 | ||
e7ef99a7 | 892 | case FQDN_HITS: |
62e76326 | 893 | Answer = snmp_var_new_integer(Var->name, Var->name_length, |
894 | FqdncacheStats.hits, | |
895 | SMI_COUNTER32); | |
896 | break; | |
897 | ||
e7ef99a7 | 898 | case FQDN_PENDHIT: |
62e76326 | 899 | /* this is now worthless */ |
900 | Answer = snmp_var_new_integer(Var->name, Var->name_length, | |
901 | 0, | |
902 | SMI_GAUGE32); | |
903 | break; | |
904 | ||
e7ef99a7 | 905 | case FQDN_NEGHIT: |
62e76326 | 906 | Answer = snmp_var_new_integer(Var->name, Var->name_length, |
907 | FqdncacheStats.negative_hits, | |
908 | SMI_COUNTER32); | |
909 | break; | |
910 | ||
e7ef99a7 | 911 | case FQDN_MISS: |
62e76326 | 912 | Answer = snmp_var_new_integer(Var->name, Var->name_length, |
913 | FqdncacheStats.misses, | |
914 | SMI_COUNTER32); | |
915 | break; | |
916 | ||
e7ef99a7 | 917 | case FQDN_GHBN: |
62e76326 | 918 | Answer = snmp_var_new_integer(Var->name, Var->name_length, |
919 | 0, /* deprecated */ | |
920 | SMI_COUNTER32); | |
921 | break; | |
922 | ||
ce75f381 | 923 | default: |
62e76326 | 924 | *ErrP = SNMP_ERR_NOSUCHNAME; |
925 | break; | |
86115da5 | 926 | } |
62e76326 | 927 | |
86115da5 | 928 | return Answer; |
ce75f381 | 929 | } |
e7ef99a7 | 930 | |
135171fe | 931 | #endif /*SQUID_SNMP */ |
de48c85e AR |
932 | |
933 | /// XXX: a hack to work around the missing DNS error info | |
934 | // see http://www.squid-cache.org/bugs/show_bug.cgi?id=2459 | |
935 | const char * | |
936 | dns_error_message_safe() | |
937 | { | |
938 | if (dns_error_message) | |
26ac0430 AJ |
939 | return dns_error_message; |
940 | debugs(35,DBG_IMPORTANT, "Internal error: lost DNS error info"); | |
941 | return "lost DNS error"; | |
de48c85e | 942 | } |