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