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