1 /*
2  * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
3  *
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.
7  */
8 
9 /* DEBUG: section 14    IP Cache */
10 
11 #include "squid.h"
12 #include "CacheManager.h"
13 #include "cbdata.h"
14 #include "dlink.h"
15 #include "dns/LookupDetails.h"
16 #include "dns/rfc3596.h"
17 #include "event.h"
18 #include "ip/Address.h"
19 #include "ip/tools.h"
20 #include "ipcache.h"
21 #include "mgr/Registration.h"
22 #include "SquidConfig.h"
23 #include "SquidTime.h"
24 #include "StatCounters.h"
25 #include "Store.h"
26 #include "util.h"
27 #include "wordlist.h"
28 
29 #if SQUID_SNMP
30 #include "snmp_core.h"
31 #endif
32 
33 /**
34  \defgroup IPCacheAPI IP Cache API
35  \ingroup Components
36  \section Introduction Introduction
37  \par
38  *  The IP cache is a built-in component of squid providing
39  *  Hostname to IP-Number translation functionality and managing
40  *  the involved data-structures. Efficiency concerns require
41  *  mechanisms that allow non-blocking access to these mappings.
42  *  The IP cache usually doesn't block on a request except for
43  *  special cases where this is desired (see below).
44  *
45  \todo IP Cache should have its own API *.h header file.
46  */
47 
48 /**
49  \defgroup IPCacheInternal IP Cache Internals
50  \ingroup IPCacheAPI
51  \todo  when IP cache is provided as a class. These sub-groups will be obsolete
52  *  for now they are used to separate the public and private functions.
53  *  with the private ones all being in IPCachInternal and public in IPCacheAPI
54  *
55  \section InternalOperation Internal Operation
56  *
57  * Internally, the execution flow is as follows: On a miss,
58  * ipcache_getnbhostbyname checks whether a request for
59  * this name is already pending, and if positive, it creates
60  * a new entry using ipcacheAddNew with the IP_PENDING
61  * flag set . Then it calls ipcacheAddPending to add a
62  * request to the queue together with data and handler.  Else,
63  * ipcache_dnsDispatch() is called to directly create a
64  * DNS query or to ipcacheEnqueue() if all no DNS port
65  * is free.  ipcache_call_pending() is called regularly
66  * to walk down the pending list and call handlers. LRU clean-up
67  * is performed through ipcache_purgelru() according to
68  * the ipcache_high threshold.
69  */
70 
71 /**
72  \ingroup IPCacheAPI
73  *
74  * The data structure used for storing name-address mappings
75  * is a small hashtable (static hash_table *ip_table),
76  * where structures of type ipcache_entry whose most
77  * interesting members are:
78  */
79 class ipcache_entry
80 {
81     MEMPROXY_CLASS(ipcache_entry);
82 
83 public:
84     ipcache_entry(const char *);
85     ~ipcache_entry();
86 
87     hash_link hash;     /* must be first */
88     time_t lastref;
89     time_t expires;
90     ipcache_addrs addrs;
91     IPH *handler;
92     void *handlerData;
93     char *error_message;
94 
95     struct timeval request_time;
96     dlink_node lru;
97     unsigned short locks;
98     struct Flags {
Flagsipcache_entry::Flags99         Flags() : negcached(false), fromhosts(false) {}
100 
101         bool negcached;
102         bool fromhosts;
103     } flags;
104 
105     int age() const; ///< time passed since request_time or -1 if unknown
106 };
107 
108 /// \ingroup IPCacheInternal
109 static struct _ipcache_stats {
110     int requests;
111     int replies;
112     int hits;
113     int misses;
114     int negative_hits;
115     int numeric_hits;
116     int rr_a;
117     int rr_aaaa;
118     int rr_cname;
119     int cname_only;
120     int invalid;
121 } IpcacheStats;
122 
123 /// \ingroup IPCacheInternal
124 static dlink_list lru_list;
125 
126 // forward-decls
127 static void stat_ipcache_get(StoreEntry *);
128 
129 static FREE ipcacheFreeEntry;
130 static IDNSCB ipcacheHandleReply;
131 static int ipcacheExpiredEntry(ipcache_entry *);
132 static ipcache_entry *ipcache_get(const char *);
133 static void ipcacheLockEntry(ipcache_entry *);
134 static void ipcacheStatPrint(ipcache_entry *, StoreEntry *);
135 static void ipcacheUnlockEntry(ipcache_entry *);
136 static void ipcacheRelease(ipcache_entry *, bool dofree = true);
137 
138 /// \ingroup IPCacheInternal
139 static ipcache_addrs static_addrs;
140 /// \ingroup IPCacheInternal
141 static hash_table *ip_table = NULL;
142 
143 /// \ingroup IPCacheInternal
144 static long ipcache_low = 180;
145 /// \ingroup IPCacheInternal
146 static long ipcache_high = 200;
147 
148 #if LIBRESOLV_DNS_TTL_HACK
149 extern int _dns_ttl_;
150 #endif
151 
152 /// \ingroup IPCacheInternal
ipcacheCount()153 inline int ipcacheCount() { return ip_table ? ip_table->count : 0; }
154 
155 int
age() const156 ipcache_entry::age() const
157 {
158     return request_time.tv_sec ? tvSubMsec(request_time, current_time) : -1;
159 }
160 
161 /**
162  \ingroup IPCacheInternal
163  *
164  * removes the given ipcache entry
165  */
166 static void
ipcacheRelease(ipcache_entry * i,bool dofree)167 ipcacheRelease(ipcache_entry * i, bool dofree)
168 {
169     if (!i) {
170         debugs(14, DBG_CRITICAL, "ipcacheRelease: Releasing entry with i=<NULL>");
171         return;
172     }
173 
174     if (!i || !i->hash.key) {
175         debugs(14, DBG_CRITICAL, "ipcacheRelease: Releasing entry without hash link!");
176         return;
177     }
178 
179     debugs(14, 3, "ipcacheRelease: Releasing entry for '" << (const char *) i->hash.key << "'");
180 
181     hash_remove_link(ip_table, (hash_link *) i);
182     dlinkDelete(&i->lru, &lru_list);
183     if (dofree)
184         ipcacheFreeEntry(i);
185 }
186 
187 /// \ingroup IPCacheInternal
188 static ipcache_entry *
ipcache_get(const char * name)189 ipcache_get(const char *name)
190 {
191     if (ip_table != NULL)
192         return (ipcache_entry *) hash_lookup(ip_table, name);
193     else
194         return NULL;
195 }
196 
197 /// \ingroup IPCacheInternal
198 static int
ipcacheExpiredEntry(ipcache_entry * i)199 ipcacheExpiredEntry(ipcache_entry * i)
200 {
201     /* all static entries are locked, so this takes care of them too */
202 
203     if (i->locks != 0)
204         return 0;
205 
206     if (i->addrs.count == 0)
207         if (0 == i->flags.negcached)
208             return 1;
209 
210     if (i->expires > squid_curtime)
211         return 0;
212 
213     return 1;
214 }
215 
216 /// \ingroup IPCacheAPI
217 void
ipcache_purgelru(void *)218 ipcache_purgelru(void *)
219 {
220     dlink_node *m;
221     dlink_node *prev = NULL;
222     ipcache_entry *i;
223     int removed = 0;
224     eventAdd("ipcache_purgelru", ipcache_purgelru, NULL, 10.0, 1);
225 
226     for (m = lru_list.tail; m; m = prev) {
227         if (ipcacheCount() < ipcache_low)
228             break;
229 
230         prev = m->prev;
231 
232         i = (ipcache_entry *)m->data;
233 
234         if (i->locks != 0)
235             continue;
236 
237         ipcacheRelease(i);
238 
239         ++removed;
240     }
241 
242     debugs(14, 9, "ipcache_purgelru: removed " << removed << " entries");
243 }
244 
245 /**
246  \ingroup IPCacheInternal
247  *
248  * purges entries added from /etc/hosts (or whatever).
249  */
250 static void
purge_entries_fromhosts(void)251 purge_entries_fromhosts(void)
252 {
253     dlink_node *m = lru_list.head;
254     ipcache_entry *i = NULL, *t;
255 
256     while (m) {
257         if (i != NULL) {    /* need to delay deletion */
258             ipcacheRelease(i);  /* we just override locks */
259             i = NULL;
260         }
261 
262         t = (ipcache_entry*)m->data;
263 
264         if (t->flags.fromhosts)
265             i = t;
266 
267         m = m->next;
268     }
269 
270     if (i != NULL)
271         ipcacheRelease(i);
272 }
273 
ipcache_entry(const char * name)274 ipcache_entry::ipcache_entry(const char *name) :
275     lastref(0),
276     expires(0),
277     handler(nullptr),
278     handlerData(nullptr),
279     error_message(nullptr),
280     locks(0) // XXX: use Lock type ?
281 {
282     hash.key = xstrdup(name);
283     Tolower(static_cast<char*>(hash.key));
284     expires = squid_curtime + Config.negativeDnsTtl;
285 
286     memset(&request_time, 0, sizeof(request_time));
287 }
288 
289 /// \ingroup IPCacheInternal
290 static void
ipcacheAddEntry(ipcache_entry * i)291 ipcacheAddEntry(ipcache_entry * i)
292 {
293     hash_link *e = (hash_link *)hash_lookup(ip_table, i->hash.key);
294 
295     if (NULL != e) {
296         /* avoid colission */
297         ipcache_entry *q = (ipcache_entry *) e;
298         ipcacheRelease(q);
299     }
300 
301     hash_join(ip_table, &i->hash);
302     dlinkAdd(i, &i->lru, &lru_list);
303     i->lastref = squid_curtime;
304 }
305 
306 /**
307  \ingroup IPCacheInternal
308  *
309  * walks down the pending list, calling handlers
310  */
311 static void
ipcacheCallback(ipcache_entry * i,int wait)312 ipcacheCallback(ipcache_entry *i, int wait)
313 {
314     IPH *callback = i->handler;
315     void *cbdata = NULL;
316     i->lastref = squid_curtime;
317 
318     if (!i->handler)
319         return;
320 
321     ipcacheLockEntry(i);
322 
323     callback = i->handler;
324 
325     i->handler = NULL;
326 
327     if (cbdataReferenceValidDone(i->handlerData, &cbdata)) {
328         const Dns::LookupDetails details(i->error_message, wait);
329         callback((i->addrs.count ? &i->addrs : NULL), details, cbdata);
330     }
331 
332     ipcacheUnlockEntry(i);
333 }
334 
335 static void
ipcacheParse(ipcache_entry * i,const rfc1035_rr * answers,int nr,const char * error_message)336 ipcacheParse(ipcache_entry *i, const rfc1035_rr * answers, int nr, const char *error_message)
337 {
338     int k;
339     int j = 0;
340     int na = 0;
341     int ttl = 0;
342     const char *name = (const char *)i->hash.key;
343     int cname_found = 0;
344 
345     i->expires = squid_curtime + Config.negativeDnsTtl;
346     i->flags.negcached = true;
347     safe_free(i->addrs.in_addrs);
348     assert(i->addrs.in_addrs == NULL);
349     safe_free(i->addrs.bad_mask);
350     assert(i->addrs.bad_mask == NULL);
351     safe_free(i->error_message);
352     assert(i->error_message == NULL);
353     i->addrs.count = 0;
354 
355     if (nr < 0) {
356         debugs(14, 3, "Lookup failed '" << error_message << "' for '" << (const char *)i->hash.key << "'");
357         i->error_message = xstrdup(error_message);
358         return;
359     }
360 
361     if (nr == 0) {
362         debugs(14, 3, "No DNS records in response to '" << name << "'");
363         i->error_message = xstrdup("No DNS records");
364         return;
365     }
366 
367     debugs(14, 3, nr << " answers for '" << name << "'");
368     assert(answers);
369 
370     for (k = 0; k < nr; ++k) {
371 
372         if (Ip::EnableIpv6 && answers[k].type == RFC1035_TYPE_AAAA) {
373             if (answers[k].rdlength != sizeof(struct in6_addr)) {
374                 debugs(14, DBG_IMPORTANT, MYNAME << "Invalid IPv6 address in response to '" << name << "'");
375                 continue;
376             }
377             ++na;
378             ++IpcacheStats.rr_aaaa;
379             continue;
380         }
381 
382         if (answers[k].type == RFC1035_TYPE_A) {
383             if (answers[k].rdlength != sizeof(struct in_addr)) {
384                 debugs(14, DBG_IMPORTANT, MYNAME << "Invalid IPv4 address in response to '" << name << "'");
385                 continue;
386             }
387             ++na;
388             ++IpcacheStats.rr_a;
389             continue;
390         }
391 
392         /* With A and AAAA, the CNAME does not necessarily come with additional records to use. */
393         if (answers[k].type == RFC1035_TYPE_CNAME) {
394             cname_found=1;
395             ++IpcacheStats.rr_cname;
396             continue;
397         }
398 
399         // otherwise its an unknown RR. debug at level 9 since we usually want to ignore these and they are common.
400         debugs(14, 9, "Unknown RR type received: type=" << answers[k].type << " starting at " << &(answers[k]) );
401     }
402     if (na == 0) {
403         debugs(14, DBG_IMPORTANT, MYNAME << "No Address records in response to '" << name << "'");
404         i->error_message = xstrdup("No Address records");
405         if (cname_found)
406             ++IpcacheStats.cname_only;
407         return;
408     }
409 
410     i->addrs.in_addrs = static_cast<Ip::Address *>(xcalloc(na, sizeof(Ip::Address)));
411     for (int l = 0; l < na; ++l)
412         i->addrs.in_addrs[l].setEmpty(); // perform same init actions as constructor would.
413     i->addrs.bad_mask = (unsigned char *)xcalloc(na, sizeof(unsigned char));
414 
415     for (j = 0, k = 0; k < nr; ++k) {
416 
417         if (answers[k].type == RFC1035_TYPE_A) {
418             if (answers[k].rdlength != sizeof(struct in_addr))
419                 continue;
420 
421             struct in_addr temp;
422             memcpy(&temp, answers[k].rdata, sizeof(struct in_addr));
423             i->addrs.in_addrs[j] = temp;
424 
425             debugs(14, 3, name << " #" << j << " " << i->addrs.in_addrs[j]);
426             ++j;
427 
428         } else if (Ip::EnableIpv6 && answers[k].type == RFC1035_TYPE_AAAA) {
429             if (answers[k].rdlength != sizeof(struct in6_addr))
430                 continue;
431 
432             struct in6_addr temp;
433             memcpy(&temp, answers[k].rdata, sizeof(struct in6_addr));
434             i->addrs.in_addrs[j] = temp;
435 
436             debugs(14, 3, name << " #" << j << " " << i->addrs.in_addrs[j] );
437             ++j;
438         }
439         if (ttl == 0 || (int) answers[k].ttl < ttl)
440             ttl = answers[k].ttl;
441     }
442 
443     assert(j == na);
444 
445     if (na < 256)
446         i->addrs.count = (unsigned char) na;
447     else
448         i->addrs.count = 255;
449 
450     if (ttl > Config.positiveDnsTtl)
451         ttl = Config.positiveDnsTtl;
452 
453     if (ttl < Config.negativeDnsTtl)
454         ttl = Config.negativeDnsTtl;
455 
456     i->expires = squid_curtime + ttl;
457 
458     i->flags.negcached = false;
459 }
460 
461 /// \ingroup IPCacheInternal
462 static void
ipcacheHandleReply(void * data,const rfc1035_rr * answers,int na,const char * error_message)463 ipcacheHandleReply(void *data, const rfc1035_rr * answers, int na, const char *error_message)
464 {
465     ipcache_entry *i;
466     static_cast<generic_cbdata *>(data)->unwrap(&i);
467     ++IpcacheStats.replies;
468     const int age = i->age();
469     statCounter.dns.svcTime.count(age);
470 
471     ipcacheParse(i, answers, na, error_message);
472     ipcacheAddEntry(i);
473     ipcacheCallback(i, age);
474 }
475 
476 /**
477  \ingroup IPCacheAPI
478  *
479  \param name        Host to resolve.
480  \param handler     Pointer to the function to be called when the reply
481  *          from the IP cache (or the DNS if the IP cache misses)
482  \param handlerData Information that is passed to the handler and does not affect the IP cache.
483  *
484  * XXX: on hits and some errors, the handler is called immediately instead
485  * of scheduling an async call. This reentrant behavior means that the
486  * user job must be extra careful after calling ipcache_nbgethostbyname,
487  * especially if the handler destroys the job. Moreover, the job has
488  * no way of knowing whether the reentrant call happened.
489  * Comm::Connection setup usually protects the job by scheduling an async call,
490  * but some user code calls ipcache_nbgethostbyname directly.
491  */
492 void
ipcache_nbgethostbyname(const char * name,IPH * handler,void * handlerData)493 ipcache_nbgethostbyname(const char *name, IPH * handler, void *handlerData)
494 {
495     ipcache_entry *i = NULL;
496     const ipcache_addrs *addrs = NULL;
497     generic_cbdata *c;
498     debugs(14, 4, "ipcache_nbgethostbyname: Name '" << name << "'.");
499     ++IpcacheStats.requests;
500 
501     if (name == NULL || name[0] == '\0') {
502         debugs(14, 4, "ipcache_nbgethostbyname: Invalid name!");
503         ++IpcacheStats.invalid;
504         const Dns::LookupDetails details("Invalid hostname", -1); // error, no lookup
505         if (handler)
506             handler(NULL, details, handlerData);
507         return;
508     }
509 
510     if ((addrs = ipcacheCheckNumeric(name))) {
511         debugs(14, 4, "ipcache_nbgethostbyname: BYPASS for '" << name << "' (already numeric)");
512         ++IpcacheStats.numeric_hits;
513         const Dns::LookupDetails details; // no error, no lookup
514         if (handler)
515             handler(addrs, details, handlerData);
516         return;
517     }
518 
519     i = ipcache_get(name);
520 
521     if (NULL == i) {
522         /* miss */
523         (void) 0;
524     } else if (ipcacheExpiredEntry(i)) {
525         /* hit, but expired -- bummer */
526         ipcacheRelease(i);
527         i = NULL;
528     } else {
529         /* hit */
530         debugs(14, 4, "ipcache_nbgethostbyname: HIT for '" << name << "'");
531 
532         if (i->flags.negcached)
533             ++IpcacheStats.negative_hits;
534         else
535             ++IpcacheStats.hits;
536 
537         i->handler = handler;
538 
539         i->handlerData = cbdataReference(handlerData);
540 
541         ipcacheCallback(i, -1); // no lookup
542 
543         return;
544     }
545 
546     debugs(14, 5, "ipcache_nbgethostbyname: MISS for '" << name << "'");
547     ++IpcacheStats.misses;
548     i = new ipcache_entry(name);
549     i->handler = handler;
550     i->handlerData = cbdataReference(handlerData);
551     i->request_time = current_time;
552     c = new generic_cbdata(i);
553     idnsALookup(hashKeyStr(&i->hash), ipcacheHandleReply, c);
554 }
555 
556 /// \ingroup IPCacheInternal
557 static void
ipcacheRegisterWithCacheManager(void)558 ipcacheRegisterWithCacheManager(void)
559 {
560     Mgr::RegisterAction("ipcache",
561                         "IP Cache Stats and Contents",
562                         stat_ipcache_get, 0, 1);
563 }
564 
565 /**
566  \ingroup IPCacheAPI
567  *
568  * Initialize the ipcache.
569  * Is called from mainInitialize() after disk initialization
570  * and prior to the reverse FQDNCache initialization
571  */
572 void
ipcache_init(void)573 ipcache_init(void)
574 {
575     int n;
576     debugs(14, DBG_IMPORTANT, "Initializing IP Cache...");
577     memset(&IpcacheStats, '\0', sizeof(IpcacheStats));
578     lru_list = dlink_list();
579 
580     static_addrs.in_addrs = static_cast<Ip::Address *>(xcalloc(1, sizeof(Ip::Address)));
581     static_addrs.in_addrs->setEmpty(); // properly setup the Ip::Address!
582     static_addrs.bad_mask = (unsigned char *)xcalloc(1, sizeof(unsigned char));
583     ipcache_high = (long) (((float) Config.ipcache.size *
584                             (float) Config.ipcache.high) / (float) 100);
585     ipcache_low = (long) (((float) Config.ipcache.size *
586                            (float) Config.ipcache.low) / (float) 100);
587     n = hashPrime(ipcache_high / 4);
588     ip_table = hash_create((HASHCMP *) strcmp, n, hash4);
589 
590     ipcacheRegisterWithCacheManager();
591 }
592 
593 /**
594  \ingroup IPCacheAPI
595  *
596  * Is different from ipcache_nbgethostbyname in that it only checks
597  * if an entry exists in the cache and does not by default contact the DNS,
598  * unless this is requested, by setting the flags.
599  *
600  \param name        Host name to resolve.
601  \param flags       Default is NULL, set to IP_LOOKUP_IF_MISS
602  *          to explicitly perform DNS lookups.
603  *
604  \retval NULL   An error occurred during lookup
605  \retval NULL   No results available in cache and no lookup specified
606  \retval *  Pointer to the ipcahce_addrs structure containing the lookup results
607  */
608 const ipcache_addrs *
ipcache_gethostbyname(const char * name,int flags)609 ipcache_gethostbyname(const char *name, int flags)
610 {
611     ipcache_entry *i = NULL;
612     ipcache_addrs *addrs;
613     assert(name);
614     debugs(14, 3, "ipcache_gethostbyname: '" << name  << "', flags=" << std::hex << flags);
615     ++IpcacheStats.requests;
616     i = ipcache_get(name);
617 
618     if (NULL == i) {
619         (void) 0;
620     } else if (ipcacheExpiredEntry(i)) {
621         ipcacheRelease(i);
622         i = NULL;
623     } else if (i->flags.negcached) {
624         ++IpcacheStats.negative_hits;
625         // ignore i->error_message: the caller just checks IP cache presence
626         return NULL;
627     } else {
628         ++IpcacheStats.hits;
629         i->lastref = squid_curtime;
630         // ignore i->error_message: the caller just checks IP cache presence
631         return &i->addrs;
632     }
633 
634     /* no entry [any more] */
635 
636     if ((addrs = ipcacheCheckNumeric(name))) {
637         ++IpcacheStats.numeric_hits;
638         return addrs;
639     }
640 
641     ++IpcacheStats.misses;
642 
643     if (flags & IP_LOOKUP_IF_MISS)
644         ipcache_nbgethostbyname(name, NULL, NULL);
645 
646     return NULL;
647 }
648 
649 /// \ingroup IPCacheInternal
650 static void
ipcacheStatPrint(ipcache_entry * i,StoreEntry * sentry)651 ipcacheStatPrint(ipcache_entry * i, StoreEntry * sentry)
652 {
653     int k;
654     char buf[MAX_IPSTRLEN];
655 
656     if (!sentry) {
657         debugs(14, DBG_CRITICAL, HERE << "CRITICAL: sentry is NULL!");
658         return;
659     }
660 
661     if (!i) {
662         debugs(14, DBG_CRITICAL, HERE << "CRITICAL: ipcache_entry is NULL!");
663         storeAppendPrintf(sentry, "CRITICAL ERROR\n");
664         return;
665     }
666 
667     int count = i->addrs.count;
668 
669     storeAppendPrintf(sentry, " %-32.32s %c%c %6d %6d %2d(%2d)",
670                       hashKeyStr(&i->hash),
671                       i->flags.fromhosts ? 'H' : ' ',
672                       i->flags.negcached ? 'N' : ' ',
673                       (int) (squid_curtime - i->lastref),
674                       (int) ((i->flags.fromhosts ? -1 : i->expires - squid_curtime)),
675                       (int) i->addrs.count,
676                       (int) i->addrs.badcount);
677 
678     /** \par
679      * Negative-cached entries have no IPs listed. */
680     if (i->flags.negcached) {
681         storeAppendPrintf(sentry, "\n");
682         return;
683     }
684 
685     /** \par
686      * Cached entries have IPs listed with a BNF of:   ip-address '-' ('OK'|'BAD') */
687     for (k = 0; k < count; ++k) {
688         /* Display tidy-up: IPv6 are so big make the list vertical */
689         if (k == 0)
690             storeAppendPrintf(sentry, " %45.45s-%3s\n",
691                               i->addrs.in_addrs[k].toStr(buf,MAX_IPSTRLEN),
692                               i->addrs.bad_mask[k] ? "BAD" : "OK ");
693         else
694             storeAppendPrintf(sentry, "%s %45.45s-%3s\n",
695                               "                                                         ", /* blank-space indenting IP list */
696                               i->addrs.in_addrs[k].toStr(buf,MAX_IPSTRLEN),
697                               i->addrs.bad_mask[k] ? "BAD" : "OK ");
698     }
699 }
700 
701 /**
702  \ingroup IPCacheInternal
703  *
704  * process objects list
705  */
706 void
stat_ipcache_get(StoreEntry * sentry)707 stat_ipcache_get(StoreEntry * sentry)
708 {
709     dlink_node *m;
710     assert(ip_table != NULL);
711     storeAppendPrintf(sentry, "IP Cache Statistics:\n");
712     storeAppendPrintf(sentry, "IPcache Entries In Use:  %d\n",
713                       ipcache_entry::UseCount());
714     storeAppendPrintf(sentry, "IPcache Entries Cached:  %d\n",
715                       ipcacheCount());
716     storeAppendPrintf(sentry, "IPcache Requests: %d\n",
717                       IpcacheStats.requests);
718     storeAppendPrintf(sentry, "IPcache Hits:            %d\n",
719                       IpcacheStats.hits);
720     storeAppendPrintf(sentry, "IPcache Negative Hits:       %d\n",
721                       IpcacheStats.negative_hits);
722     storeAppendPrintf(sentry, "IPcache Numeric Hits:        %d\n",
723                       IpcacheStats.numeric_hits);
724     storeAppendPrintf(sentry, "IPcache Misses:          %d\n",
725                       IpcacheStats.misses);
726     storeAppendPrintf(sentry, "IPcache Retrieved A:     %d\n",
727                       IpcacheStats.rr_a);
728     storeAppendPrintf(sentry, "IPcache Retrieved AAAA:  %d\n",
729                       IpcacheStats.rr_aaaa);
730     storeAppendPrintf(sentry, "IPcache Retrieved CNAME: %d\n",
731                       IpcacheStats.rr_cname);
732     storeAppendPrintf(sentry, "IPcache CNAME-Only Response: %d\n",
733                       IpcacheStats.cname_only);
734     storeAppendPrintf(sentry, "IPcache Invalid Request: %d\n",
735                       IpcacheStats.invalid);
736     storeAppendPrintf(sentry, "\n\n");
737     storeAppendPrintf(sentry, "IP Cache Contents:\n\n");
738     storeAppendPrintf(sentry, " %-31.31s %3s %6s %6s  %4s\n",
739                       "Hostname",
740                       "Flg",
741                       "lstref",
742                       "TTL",
743                       "N(b)");
744 
745     for (m = lru_list.head; m; m = m->next) {
746         assert( m->next != m );
747         ipcacheStatPrint((ipcache_entry *)m->data, sentry);
748     }
749 }
750 
751 /// \ingroup IPCacheAPI
752 void
ipcacheInvalidate(const char * name)753 ipcacheInvalidate(const char *name)
754 {
755     ipcache_entry *i;
756 
757     if ((i = ipcache_get(name)) == NULL)
758         return;
759 
760     i->expires = squid_curtime;
761 
762     /*
763      * NOTE, don't call ipcacheRelease here because we might be here due
764      * to a thread started from a callback.
765      */
766 }
767 
768 /// \ingroup IPCacheAPI
769 void
ipcacheInvalidateNegative(const char * name)770 ipcacheInvalidateNegative(const char *name)
771 {
772     ipcache_entry *i;
773 
774     if ((i = ipcache_get(name)) == NULL)
775         return;
776 
777     if (i->flags.negcached)
778         i->expires = squid_curtime;
779 
780     /*
781      * NOTE, don't call ipcacheRelease here because we might be here due
782      * to a thread started from a callback.
783      */
784 }
785 
786 /// \ingroup IPCacheAPI
787 ipcache_addrs *
ipcacheCheckNumeric(const char * name)788 ipcacheCheckNumeric(const char *name)
789 {
790     Ip::Address ip;
791     /* check if it's already a IP address in text form. */
792 
793     /* it may be IPv6-wrapped */
794     if (name[0] == '[') {
795         char *tmp = xstrdup(&name[1]);
796         tmp[strlen(tmp)-1] = '\0';
797         if (!(ip = tmp)) {
798             delete tmp;
799             return NULL;
800         }
801         delete tmp;
802     } else if (!(ip = name))
803         return NULL;
804 
805     debugs(14, 4, "ipcacheCheckNumeric: HIT_BYPASS for '" << name << "' == " << ip );
806 
807     static_addrs.count = 1;
808 
809     static_addrs.cur = 0;
810 
811     static_addrs.in_addrs[0] = ip;
812 
813     static_addrs.bad_mask[0] = FALSE;
814 
815     static_addrs.badcount = 0;
816 
817     return &static_addrs;
818 }
819 
820 /// \ingroup IPCacheInternal
821 static void
ipcacheLockEntry(ipcache_entry * i)822 ipcacheLockEntry(ipcache_entry * i)
823 {
824     if (i->locks++ == 0) {
825         dlinkDelete(&i->lru, &lru_list);
826         dlinkAdd(i, &i->lru, &lru_list);
827     }
828 }
829 
830 /// \ingroup IPCacheInternal
831 static void
ipcacheUnlockEntry(ipcache_entry * i)832 ipcacheUnlockEntry(ipcache_entry * i)
833 {
834     if (i->locks < 1) {
835         debugs(14, DBG_IMPORTANT, "WARNING: ipcacheEntry unlocked with no lock! locks=" << i->locks);
836         return;
837     }
838 
839     -- i->locks;
840 
841     if (ipcacheExpiredEntry(i))
842         ipcacheRelease(i);
843 }
844 
845 /// \ingroup IPCacheAPI
846 void
ipcacheCycleAddr(const char * name,ipcache_addrs * ia)847 ipcacheCycleAddr(const char *name, ipcache_addrs * ia)
848 {
849     ipcache_entry *i;
850     unsigned char k;
851     assert(name || ia);
852 
853     if (NULL == ia) {
854         if ((i = ipcache_get(name)) == NULL)
855             return;
856 
857         if (i->flags.negcached)
858             return;
859 
860         ia = &i->addrs;
861     }
862 
863     for (k = 0; k < ia->count; ++k) {
864         if (++ia->cur == ia->count)
865             ia->cur = 0;
866 
867         if (!ia->bad_mask[ia->cur])
868             break;
869     }
870 
871     if (k == ia->count) {
872         /* All bad, reset to All good */
873         debugs(14, 3, "ipcacheCycleAddr: Changing ALL " << name << " addrs from BAD to OK");
874 
875         for (k = 0; k < ia->count; ++k)
876             ia->bad_mask[k] = 0;
877 
878         ia->badcount = 0;
879 
880         ia->cur = 0;
881     }
882 
883     /* NP: zero-based so we increase the human-readable number of our position */
884     debugs(14, 3, "ipcacheCycleAddr: " << name << " now at " << ia->in_addrs[ia->cur] << " (" << (ia->cur+1) << " of " << ia->count << ")");
885 }
886 
887 /**
888  \ingroup IPCacheAPI
889  *
890  \param name    domain name to have an IP marked bad
891  \param addr    specific addres to be marked bad
892  */
893 void
ipcacheMarkBadAddr(const char * name,const Ip::Address & addr)894 ipcacheMarkBadAddr(const char *name, const Ip::Address &addr)
895 {
896     ipcache_entry *i;
897     ipcache_addrs *ia;
898     int k;
899 
900     /** Does nothing if the domain name does not exist. */
901     if ((i = ipcache_get(name)) == NULL)
902         return;
903 
904     ia = &i->addrs;
905 
906     for (k = 0; k < (int) ia->count; ++k) {
907         if (addr == ia->in_addrs[k] )
908             break;
909     }
910 
911     /** Does nothing if the IP does not exist for the doamin. */
912     if (k == (int) ia->count)
913         return;
914 
915     /** Marks the given address as BAD */
916     if (!ia->bad_mask[k]) {
917         ia->bad_mask[k] = TRUE;
918         ++ia->badcount;
919         debugs(14, 2, "ipcacheMarkBadAddr: " << name << " " << addr );
920     }
921 
922     /** then calls ipcacheCycleAddr() to advance the current pointer to the next OK address. */
923     ipcacheCycleAddr(name, ia);
924 }
925 
926 /// \ingroup IPCacheAPI
927 void
ipcacheMarkAllGood(const char * name)928 ipcacheMarkAllGood(const char *name)
929 {
930     ipcache_entry *i;
931     ipcache_addrs *ia;
932     int k;
933 
934     if ((i = ipcache_get(name)) == NULL)
935         return;
936 
937     ia = &i->addrs;
938 
939     /* All bad, reset to All good */
940     debugs(14, 3, "ipcacheMarkAllGood: Changing ALL " << name << " addrs to OK (" << ia->badcount << "/" << ia->count << " bad)");
941 
942     for (k = 0; k < ia->count; ++k)
943         ia->bad_mask[k] = 0;
944 
945     ia->badcount = 0;
946 }
947 
948 /// \ingroup IPCacheAPI
949 void
ipcacheMarkGoodAddr(const char * name,const Ip::Address & addr)950 ipcacheMarkGoodAddr(const char *name, const Ip::Address &addr)
951 {
952     ipcache_entry *i;
953     ipcache_addrs *ia;
954     int k;
955 
956     if ((i = ipcache_get(name)) == NULL)
957         return;
958 
959     ia = &i->addrs;
960 
961     for (k = 0; k < (int) ia->count; ++k) {
962         if (addr == ia->in_addrs[k])
963             break;
964     }
965 
966     if (k == (int) ia->count)   /* not found */
967         return;
968 
969     if (!ia->bad_mask[k])   /* already OK */
970         return;
971 
972     ia->bad_mask[k] = FALSE;
973 
974     -- ia->badcount;
975 
976     debugs(14, 2, "ipcacheMarkGoodAddr: " << name << " " << addr );
977 }
978 
979 /// \ingroup IPCacheInternal
980 static void
ipcacheFreeEntry(void * data)981 ipcacheFreeEntry(void *data)
982 {
983     ipcache_entry *i = (ipcache_entry *)data;
984     delete i;
985 }
986 
~ipcache_entry()987 ipcache_entry::~ipcache_entry()
988 {
989     xfree(addrs.in_addrs);
990     xfree(addrs.bad_mask);
991     xfree(error_message);
992     xfree(hash.key);
993 }
994 
995 /// \ingroup IPCacheAPI
996 void
ipcacheFreeMemory(void)997 ipcacheFreeMemory(void)
998 {
999     hashFreeItems(ip_table, ipcacheFreeEntry);
1000     hashFreeMemory(ip_table);
1001     ip_table = NULL;
1002 }
1003 
1004 /**
1005  \ingroup IPCacheAPI
1006  *
1007  * Recalculate IP cache size upon reconfigure.
1008  * Is called to clear the IPCache's data structures,
1009  * cancel all pending requests.
1010  */
1011 void
ipcache_restart(void)1012 ipcache_restart(void)
1013 {
1014     ipcache_high = (long) (((float) Config.ipcache.size *
1015                             (float) Config.ipcache.high) / (float) 100);
1016     ipcache_low = (long) (((float) Config.ipcache.size *
1017                            (float) Config.ipcache.low) / (float) 100);
1018     purge_entries_fromhosts();
1019 }
1020 
1021 /**
1022  \ingroup IPCacheAPI
1023  *
1024  * Adds a "static" entry from /etc/hosts
1025  *
1026  \param name    Hostname to be linked with IP
1027  \param ipaddr  IP Address to be cached.
1028  *
1029  \retval 0  Success.
1030  \retval 1  IP address is invalid or other error.
1031  */
1032 int
ipcacheAddEntryFromHosts(const char * name,const char * ipaddr)1033 ipcacheAddEntryFromHosts(const char *name, const char *ipaddr)
1034 {
1035     ipcache_entry *i;
1036 
1037     Ip::Address ip;
1038 
1039     if (!(ip = ipaddr)) {
1040         if (strchr(ipaddr, ':') && strspn(ipaddr, "0123456789abcdefABCDEF:") == strlen(ipaddr)) {
1041             debugs(14, 3, "ipcacheAddEntryFromHosts: Skipping IPv6 address '" << ipaddr << "'");
1042         } else {
1043             debugs(14, DBG_IMPORTANT, "ipcacheAddEntryFromHosts: Bad IP address '" << ipaddr << "'");
1044         }
1045 
1046         return 1;
1047     }
1048 
1049     if ((i = ipcache_get(name))) {
1050         if (1 == i->flags.fromhosts) {
1051             ipcacheUnlockEntry(i);
1052         } else if (i->locks > 0) {
1053             debugs(14, DBG_IMPORTANT, "ipcacheAddEntryFromHosts: can't add static entry for locked name '" << name << "'");
1054             return 1;
1055         } else {
1056             ipcacheRelease(i);
1057         }
1058     }
1059 
1060     i = new ipcache_entry(name);
1061     i->addrs.count = 1;
1062     i->addrs.cur = 0;
1063     i->addrs.badcount = 0;
1064 
1065     i->addrs.in_addrs = static_cast<Ip::Address *>(xcalloc(1, sizeof(Ip::Address)));
1066     i->addrs.bad_mask = (unsigned char *)xcalloc(1, sizeof(unsigned char));
1067     i->addrs.in_addrs[0] = ip;
1068     i->addrs.bad_mask[0] = FALSE;
1069     i->flags.fromhosts = true;
1070     ipcacheAddEntry(i);
1071     ipcacheLockEntry(i);
1072     return 0;
1073 }
1074 
1075 #if SQUID_SNMP
1076 /**
1077  \ingroup IPCacheAPI
1078  *
1079  * The function to return the ip cache statistics to via SNMP
1080  */
1081 variable_list *
snmp_netIpFn(variable_list * Var,snint * ErrP)1082 snmp_netIpFn(variable_list * Var, snint * ErrP)
1083 {
1084     variable_list *Answer = NULL;
1085     MemBuf tmp;
1086     debugs(49, 5, "snmp_netIpFn: Processing request:" << snmpDebugOid(Var->name, Var->name_length, tmp));
1087     *ErrP = SNMP_ERR_NOERROR;
1088 
1089     switch (Var->name[LEN_SQ_NET + 1]) {
1090 
1091     case IP_ENT:
1092         Answer = snmp_var_new_integer(Var->name, Var->name_length,
1093                                       ipcacheCount(),
1094                                       SMI_GAUGE32);
1095         break;
1096 
1097     case IP_REQ:
1098         Answer = snmp_var_new_integer(Var->name, Var->name_length,
1099                                       IpcacheStats.requests,
1100                                       SMI_COUNTER32);
1101         break;
1102 
1103     case IP_HITS:
1104         Answer = snmp_var_new_integer(Var->name, Var->name_length,
1105                                       IpcacheStats.hits,
1106                                       SMI_COUNTER32);
1107         break;
1108 
1109     case IP_PENDHIT:
1110         Answer = snmp_var_new_integer(Var->name, Var->name_length,
1111                                       0,
1112                                       SMI_GAUGE32);
1113         break;
1114 
1115     case IP_NEGHIT:
1116         Answer = snmp_var_new_integer(Var->name, Var->name_length,
1117                                       IpcacheStats.negative_hits,
1118                                       SMI_COUNTER32);
1119         break;
1120 
1121     case IP_MISS:
1122         Answer = snmp_var_new_integer(Var->name, Var->name_length,
1123                                       IpcacheStats.misses,
1124                                       SMI_COUNTER32);
1125         break;
1126 
1127     case IP_GHBN:
1128         Answer = snmp_var_new_integer(Var->name, Var->name_length,
1129                                       0, /* deprecated */
1130                                       SMI_COUNTER32);
1131         break;
1132 
1133     case IP_LOC:
1134         Answer = snmp_var_new_integer(Var->name, Var->name_length,
1135                                       0, /* deprecated */
1136                                       SMI_COUNTER32);
1137         break;
1138 
1139     default:
1140         *ErrP = SNMP_ERR_NOSUCHNAME;
1141         snmp_var_free(Answer);
1142         return (NULL);
1143     }
1144 
1145     return Answer;
1146 }
1147 
1148 #endif /*SQUID_SNMP */
1149 
1150