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