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