-
/*
- * $Id: ipcache.cc,v 1.228 2000/12/30 23:29:06 wessels Exp $
- *
* DEBUG: section 14 IP Cache
* AUTHOR: Harvest Derived
*
- * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
+ * SQUID Web Proxy Cache http://www.squid-cache.org/
* ----------------------------------------------------------
*
- * Squid is the result of efforts by numerous individuals from the
- * Internet community. Development is led by Duane Wessels of the
- * National Laboratory for Applied Network Research and funded by the
- * National Science Foundation. Squid is Copyrighted (C) 1998 by
- * the Regents of the University of California. Please see the
- * COPYRIGHT file for full details. Squid incorporates software
- * developed and/or copyrighted by other sources. Please see the
- * CREDITS file for full details.
+ * Squid is the result of efforts by numerous individuals from
+ * the Internet community; see the CONTRIBUTORS file for full
+ * details. Many organizations have provided support for Squid's
+ * development; see the SPONSORS file for full details. Squid is
+ * Copyrighted (C) 2001 by the Regents of the University of
+ * California; see the COPYRIGHT file for full details. Squid
+ * incorporates software developed and/or copyrighted by other
+ * sources; see the CREDITS file for full details.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
*/
#include "squid.h"
+#include "cbdata.h"
+#include "event.h"
+#include "CacheManager.h"
+#include "SquidTime.h"
+#include "Store.h"
+#include "wordlist.h"
+#include "ip/IpAddress.h"
-typedef struct _ipcache_entry ipcache_entry;
+/**
+ \defgroup IPCacheAPI IP Cache API
+ \ingroup Components
+ \section Introduction Introduction
+ \par
+ * The IP cache is a built-in component of squid providing
+ * Hostname to IP-Number translation functionality and managing
+ * the involved data-structures. Efficiency concerns require
+ * mechanisms that allow non-blocking access to these mappings.
+ * The IP cache usually doesn't block on a request except for
+ * special cases where this is desired (see below).
+ *
+ \todo IP Cache should have its own API *.h header file.
+ */
-struct _ipcache_entry {
+/**
+ \defgroup IPCacheInternal IP Cache Internals
+ \ingroup IPCacheAPI
+ \todo when IP cache is provided as a class. These sub-groups will be obsolete
+ * for now they are used to seperate the public and private functions.
+ * with the private ones all being in IPCachInternal and public in IPCacheAPI
+ *
+ \section InternalOperation Internal Operation
+ *
+ * Internally, the execution flow is as follows: On a miss,
+ * ipcache_getnbhostbyname checks whether a request for
+ * this name is already pending, and if positive, it creates
+ * a new entry using ipcacheAddNew with the IP_PENDING
+ * flag set . Then it calls ipcacheAddPending to add a
+ * request to the queue together with data and handler. Else,
+ * ipcache_dnsDispatch() is called to directly create a
+ * DNS query or to ipcacheEnqueue() if all no DNS port
+ * is free. ipcache_call_pending() is called regularly
+ * to walk down the pending list and call handlers. LRU clean-up
+ * is performed through ipcache_purgelru() according to
+ * the ipcache_high threshold.
+ */
+
+/**
+ \ingroup IPCacheAPI
+ *
+ * The data structure used for storing name-address mappings
+ * is a small hashtable (static hash_table *ip_table),
+ * where structures of type ipcache_entry whose most
+ * interesting members are:
+ */
+class ipcache_entry
+{
+public:
hash_link hash; /* must be first */
time_t lastref;
time_t expires;
IPH *handler;
void *handlerData;
char *error_message;
+
struct timeval request_time;
dlink_node lru;
unsigned short locks;
+#if DNS_CNAME
+ unsigned short cname_wait;
+#endif
+
struct {
- unsigned int negcached:1;
- unsigned int fromhosts:1;
+ unsigned int negcached:1;
+ unsigned int fromhosts:1;
} flags;
+
+ int age() const; ///< time passed since request_time or -1 if unknown
};
-static struct {
+/// \ingroup IPCacheInternal
+static struct _ipcache_stats {
int requests;
int replies;
int hits;
int misses;
int negative_hits;
- int errors;
- int ghbn_calls; /* # calls to blocking gethostbyname() */
- int release_locked;
+ int numeric_hits;
+ int rr_a;
+ int rr_aaaa;
+ int rr_cname;
+ int cname_only;
+ int invalid;
} IpcacheStats;
+/// \ingroup IPCacheInternal
static dlink_list lru_list;
static FREE ipcacheFreeEntry;
#else
static IDNSCB ipcacheHandleReply;
#endif
-static IPH dummy_handler;
+static IPH ipcacheHandleCnameRecurse;
static int ipcacheExpiredEntry(ipcache_entry *);
-static int ipcache_testname(void);
#if USE_DNSSERVERS
-static ipcache_entry *ipcacheParse(const char *buf);
+static int ipcacheParse(ipcache_entry *, const char *buf);
#else
-static ipcache_entry *ipcacheParse(rfc1035_rr *, int);
+static int ipcacheParse(ipcache_entry *, rfc1035_rr *, int, const char *error);
#endif
static ipcache_entry *ipcache_get(const char *);
static void ipcacheLockEntry(ipcache_entry *);
static void ipcacheStatPrint(ipcache_entry *, StoreEntry *);
static void ipcacheUnlockEntry(ipcache_entry *);
-static void ipcacheRelease(ipcache_entry *);
+static void ipcacheRelease(ipcache_entry *, bool dofree = true);
+/// \ingroup IPCacheInternal
static ipcache_addrs static_addrs;
+/// \ingroup IPCacheInternal
static hash_table *ip_table = NULL;
+/// \ingroup IPCacheInternal
static long ipcache_low = 180;
+/// \ingroup IPCacheInternal
static long ipcache_high = 200;
#if LIBRESOLV_DNS_TTL_HACK
extern int _dns_ttl_;
#endif
-static int
-ipcache_testname(void)
+int
+ipcache_entry::age() const
{
- wordlist *w = NULL;
- debug(14, 1) ("Performing DNS Tests...\n");
- if ((w = Config.dns_testname_list) == NULL)
- return 1;
- for (; w; w = w->next) {
- IpcacheStats.ghbn_calls++;
- if (gethostbyname(w->key) != NULL)
- return 1;
- }
- return 0;
+ return request_time.tv_sec ? tvSubMsec(request_time, current_time) : -1;
}
-/* removes the given ipcache entry */
+
+
+/**
+ \ingroup IPCacheInternal
+ *
+ * removes the given ipcache entry
+ */
static void
-ipcacheRelease(ipcache_entry * i)
+ipcacheRelease(ipcache_entry * i, bool dofree)
{
+ if (!i) {
+ debugs(14, 0, "ipcacheRelease: Releasing entry with i=<NULL>");
+ return;
+ }
+
+ if (!i || !i->hash.key) {
+ debugs(14, 0, "ipcacheRelease: Releasing entry without hash link!");
+ return;
+ }
+
+ debugs(14, 3, "ipcacheRelease: Releasing entry for '" << (const char *) i->hash.key << "'");
+
hash_remove_link(ip_table, (hash_link *) i);
dlinkDelete(&i->lru, &lru_list);
- ipcacheFreeEntry(i);
+ if (dofree)
+ ipcacheFreeEntry(i);
}
+/// \ingroup IPCacheInternal
static ipcache_entry *
ipcache_get(const char *name)
{
if (ip_table != NULL)
- return (ipcache_entry *) hash_lookup(ip_table, name);
+ return (ipcache_entry *) hash_lookup(ip_table, name);
else
- return NULL;
+ return NULL;
}
+/// \ingroup IPCacheInternal
static int
ipcacheExpiredEntry(ipcache_entry * i)
{
/* all static entries are locked, so this takes care of them too */
+
if (i->locks != 0)
- return 0;
+ return 0;
+
if (i->addrs.count == 0)
- if (0 == i->flags.negcached)
- return 1;
+ if (0 == i->flags.negcached)
+ return 1;
+
if (i->expires > squid_curtime)
- return 0;
+ return 0;
+
return 1;
}
+/// \ingroup IPCacheAPI
void
ipcache_purgelru(void *voidnotused)
{
ipcache_entry *i;
int removed = 0;
eventAdd("ipcache_purgelru", ipcache_purgelru, NULL, 10.0, 1);
+
for (m = lru_list.tail; m; m = prev) {
- if (memInUse(MEM_IPCACHE_ENTRY) < ipcache_low)
- break;
- prev = m->prev;
- i = m->data;
- if (i->locks != 0)
- continue;
- ipcacheRelease(i);
- removed++;
+ if (memInUse(MEM_IPCACHE_ENTRY) < ipcache_low)
+ break;
+
+ prev = m->prev;
+
+ i = (ipcache_entry *)m->data;
+
+ if (i->locks != 0)
+ continue;
+
+ ipcacheRelease(i);
+
+ removed++;
}
- debug(14, 9) ("ipcache_purgelru: removed %d entries\n", removed);
+
+ debugs(14, 9, "ipcache_purgelru: removed " << removed << " entries");
}
-/* purges entries added from /etc/hosts (or whatever). */
+/**
+ \ingroup IPCacheInternal
+ *
+ * purges entries added from /etc/hosts (or whatever).
+ */
static void
purge_entries_fromhosts(void)
{
dlink_node *m = lru_list.head;
ipcache_entry *i = NULL, *t;
+
while (m) {
- if (i != NULL) { /* need to delay deletion */
- ipcacheRelease(i); /* we just override locks */
- i = NULL;
- }
- t = m->data;
- if (t->flags.fromhosts)
- i = t;
- m = m->next;
+ if (i != NULL) { /* need to delay deletion */
+ ipcacheRelease(i); /* we just override locks */
+ i = NULL;
+ }
+
+ t = (ipcache_entry*)m->data;
+
+ if (t->flags.fromhosts)
+ i = t;
+
+ m = m->next;
}
+
if (i != NULL)
- ipcacheRelease(i);
+ ipcacheRelease(i);
}
-/* create blank ipcache_entry */
+/**
+ \ingroup IPCacheInternal
+ *
+ * create blank ipcache_entry
+ */
static ipcache_entry *
ipcacheCreateEntry(const char *name)
{
static ipcache_entry *i;
- i = memAllocate(MEM_IPCACHE_ENTRY);
+ i = (ipcache_entry *)memAllocate(MEM_IPCACHE_ENTRY);
i->hash.key = xstrdup(name);
i->expires = squid_curtime + Config.negativeDnsTtl;
return i;
}
+/// \ingroup IPCacheInternal
static void
ipcacheAddEntry(ipcache_entry * i)
{
- hash_link *e = hash_lookup(ip_table, i->hash.key);
+ hash_link *e = (hash_link *)hash_lookup(ip_table, i->hash.key);
+
+#if DNS_CNAME
+ /* INET6 : should NOT be adding this entry until all CNAME have been received. */
+ assert(i->cname_wait == 0);
+#endif
+
if (NULL != e) {
- /* avoid colission */
- ipcache_entry *q = (ipcache_entry *) e;
- ipcacheRelease(q);
+ /* avoid colission */
+ ipcache_entry *q = (ipcache_entry *) e;
+#if DNS_CNAME
+ if (q == i) {
+ /* can occur with Multiple-depth CNAME Recursion if parent returned early with additional */
+ /* just need to drop from the hash without releasing actual memory */
+ ipcacheRelease(q, false);
+ } else
+#endif
+ ipcacheRelease(q);
}
+
hash_join(ip_table, &i->hash);
dlinkAdd(i, &i->lru, &lru_list);
i->lastref = squid_curtime;
}
-/* walks down the pending list, calling handlers */
+/**
+ \ingroup IPCacheInternal
+ *
+ * walks down the pending list, calling handlers
+ */
static void
-ipcacheCallback(ipcache_entry * i)
+ipcacheCallback(ipcache_entry *i, int wait)
{
- IPH *handler = i->handler;
- void *handlerData = i->handlerData;
+ IPH *callback = i->handler;
+ void *cbdata = NULL;
i->lastref = squid_curtime;
+
+ if (!i->handler)
+ return;
+
ipcacheLockEntry(i);
- if (NULL == handler)
- return;
+
+ callback = i->handler;
+
i->handler = NULL;
- i->handlerData = NULL;
- if (cbdataValid(handlerData)) {
- dns_error_message = i->error_message;
- handler(i->flags.negcached ? NULL : &i->addrs, handlerData);
+
+ if (cbdataReferenceValidDone(i->handlerData, &cbdata)) {
+ const DnsLookupDetails details(i->error_message, wait);
+ callback((i->addrs.count ? &i->addrs : NULL), details, cbdata);
}
- cbdataUnlock(handlerData);
+
ipcacheUnlockEntry(i);
}
-static ipcache_entry *
+/// \ingroup IPCacheAPI
#if USE_DNSSERVERS
-ipcacheParse(const char *inbuf)
+static int
+ipcacheParse(ipcache_entry *i, const char *inbuf)
{
LOCAL_ARRAY(char, buf, DNS_INBUF_SZ);
char *token;
- static ipcache_entry i;
- int j;
- int k;
int ipcount = 0;
int ttl;
- char A[32][16];
- memset(&i, '\0', sizeof(i));
- i.expires = squid_curtime;
- i.flags.negcached = 1;
+ char *A[32];
+ const char *name = (const char *)i->hash.key;
+ i->expires = squid_curtime + Config.negativeDnsTtl;
+ i->flags.negcached = 1;
+ safe_free(i->addrs.in_addrs);
+ safe_free(i->addrs.bad_mask);
+ safe_free(i->error_message);
+ i->addrs.count = 0;
+
if (inbuf == NULL) {
- debug(14, 1) ("ipcacheParse: Got <NULL> reply\n");
- i.error_message = xstrdup("Internal Squid Error");
- return &i;
+ debugs(14, 1, "ipcacheParse: Got <NULL> reply");
+ i->error_message = xstrdup("Internal Error");
+ return -1;
}
+
xstrncpy(buf, inbuf, DNS_INBUF_SZ);
- debug(14, 5) ("ipcacheParse: parsing: {%s}\n", buf);
+ debugs(14, 5, "ipcacheParse: parsing: {" << buf << "}");
token = strtok(buf, w_space);
+
if (NULL == token) {
- debug(14, 1) ("ipcacheParse: Got <NULL>, expecting '$addr'\n");
- return &i;
+ debugs(14, 1, "ipcacheParse: expecting result, got '" << inbuf << "'");
+
+ i->error_message = xstrdup("Internal Error");
+ return -1;
}
+
if (0 == strcmp(token, "$fail")) {
- i.expires = squid_curtime + Config.negativeDnsTtl;
- token = strtok(NULL, "\n");
- assert(NULL != token);
- i.error_message = xstrdup(token);
- return &i;
+ token = strtok(NULL, "\n");
+ assert(NULL != token);
+ i->error_message = xstrdup(token);
+ return 0;
}
+
if (0 != strcmp(token, "$addr")) {
- debug(14, 1) ("ipcacheParse: Got '%s', expecting '$addr'\n", token);
- return &i;
+ debugs(14, 1, "ipcacheParse: expecting '$addr', got '" << inbuf << "' in response to '" << name << "'");
+
+ i->error_message = xstrdup("Internal Error");
+ return -1;
}
+
token = strtok(NULL, w_space);
+
if (NULL == token) {
- debug(14, 1) ("ipcacheParse: Got <NULL>, expecting TTL\n");
- return &i;
+ debugs(14, 1, "ipcacheParse: expecting TTL, got '" << inbuf << "' in response to '" << name << "'");
+
+ i->error_message = xstrdup("Internal Error");
+ return -1;
}
- i.flags.negcached = 0;
+
ttl = atoi(token);
- if (ttl > 0)
- i.expires = squid_curtime + ttl;
- else
- i.expires = squid_curtime + Config.positiveDnsTtl;
+
while (NULL != (token = strtok(NULL, w_space))) {
- xstrncpy(A[ipcount], token, 16);
- if (++ipcount == 32)
- break;
+ A[ipcount] = token;
+
+ if (++ipcount == 32)
+ break;
}
- if (0 == ipcount) {
- i.addrs.in_addrs = NULL;
- i.addrs.bad_mask = NULL;
- } else {
- i.addrs.in_addrs = xcalloc(ipcount, sizeof(struct in_addr));
- i.addrs.bad_mask = xcalloc(ipcount, sizeof(unsigned char));
+
+ if (ipcount > 0) {
+ int j, k;
+
+ i->addrs.in_addrs = (IpAddress *)xcalloc(ipcount, sizeof(IpAddress));
+ for (int l = 0; l < ipcount; l++)
+ i->addrs.in_addrs[l].SetEmpty(); // perform same init actions as constructor would.
+ i->addrs.bad_mask = (unsigned char *)xcalloc(ipcount, sizeof(unsigned char));
+ memset(i->addrs.bad_mask, 0, sizeof(unsigned char) * ipcount);
+
+ for (j = 0, k = 0; k < ipcount; k++) {
+ if ( i->addrs.in_addrs[j] = A[k] )
+ j++;
+ else
+ debugs(14, 1, "ipcacheParse: Invalid IP address '" << A[k] << "' in response to '" << name << "'");
+ }
+
+ i->addrs.count = (unsigned char) j;
}
- for (j = 0, k = 0; k < ipcount; k++) {
- if (safe_inet_addr(A[k], &i.addrs.in_addrs[j]))
- j++;
- else
- debug(14, 1) ("ipcacheParse: Invalid IP address '%s'\n", A[k]);
+
+ if (i->addrs.count <= 0) {
+ debugs(14, 1, "ipcacheParse: No addresses in response to '" << name << "'");
+ return -1;
}
- i.addrs.count = (unsigned char) j;
- return &i;
+
+ if (ttl == 0 || ttl > Config.positiveDnsTtl)
+ ttl = Config.positiveDnsTtl;
+
+ if (ttl < Config.negativeDnsTtl)
+ ttl = Config.negativeDnsTtl;
+
+ i->expires = squid_curtime + ttl;
+
+ i->flags.negcached = 0;
+
+ return i->addrs.count;
}
+
#else
-ipcacheParse(rfc1035_rr * answers, int nr)
+static int
+ipcacheParse(ipcache_entry *i, rfc1035_rr * answers, int nr, const char *error_message)
{
- static ipcache_entry i;
int k;
- int j;
+ int j = 0;
int na = 0;
- memset(&i, '\0', sizeof(i));
- i.expires = squid_curtime + Config.negativeDnsTtl;
- i.flags.negcached = 1;
+ int ttl = 0;
+ const char *name = (const char *)i->hash.key;
+ int cname_found = 0;
+
+ i->expires = squid_curtime + Config.negativeDnsTtl;
+ i->flags.negcached = 1;
+ safe_free(i->addrs.in_addrs);
+ assert(i->addrs.in_addrs == NULL);
+ safe_free(i->addrs.bad_mask);
+ assert(i->addrs.bad_mask == NULL);
+ safe_free(i->error_message);
+ assert(i->error_message == NULL);
+ i->addrs.count = 0;
+
if (nr < 0) {
- debug(14, 3) ("ipcacheParse: Lookup failed (error %d)\n",
- rfc1035_errno);
- assert(rfc1035_error_message);
- i.error_message = xstrdup(rfc1035_error_message);
- return &i;
+ debugs(14, 3, "ipcacheParse: Lookup failed '" << error_message << "' for '" << (const char *)i->hash.key << "'");
+ i->error_message = xstrdup(error_message);
+ return -1;
}
+
if (nr == 0) {
- debug(14, 3) ("ipcacheParse: No DNS records\n");
- i.error_message = xstrdup("No DNS records");
- return &i;
+ debugs(14, 3, "ipcacheParse: No DNS records in response to '" << name << "'");
+ i->error_message = xstrdup("No DNS records");
+ return -1;
}
+
assert(answers);
- for (j = 0, k = 0; k < nr; k++) {
- if (answers[k].type != RFC1035_TYPE_A)
- continue;
- if (answers[k].class != RFC1035_CLASS_IN)
- continue;
- na++;
+
+ for (k = 0; k < nr; k++) {
+
+#if USE_IPV6
+ if (answers[k].type == RFC1035_TYPE_AAAA) {
+ if (answers[k].rdlength != sizeof(struct in6_addr)) {
+ debugs(14, 1, "ipcacheParse: Invalid IPv6 address in response to '" << name << "'");
+ continue;
+ }
+ na++;
+ IpcacheStats.rr_aaaa++;
+ continue;
+ }
+#endif
+
+ if (answers[k].type == RFC1035_TYPE_A) {
+ if (answers[k].rdlength != sizeof(struct in_addr)) {
+ debugs(14, 1, "ipcacheParse: Invalid IPv4 address in response to '" << name << "'");
+ continue;
+ }
+ na++;
+ IpcacheStats.rr_a++;
+ continue;
+ }
+
+ /* With A and AAAA, the CNAME does not necessarily come with additional records to use. */
+ if (answers[k].type == RFC1035_TYPE_CNAME) {
+ cname_found=1;
+ IpcacheStats.rr_cname++;
+
+#if DNS_CNAME
+ debugs(14, 5, "ipcacheParse: " << name << " CNAME " << answers[k].rdata << " (checking destination: " << i << ").");
+ const ipcache_addrs *res = ipcache_gethostbyname(answers[k].rdata, 0);
+ if (res) {
+ na += res->count;
+ debugs(14, 5, "ipcacheParse: CNAME " << answers[k].rdata << " already has " << res->count << " IPs cached.");
+ } else {
+ /* keep going on this, but flag the fact that we need to wait for a CNAME lookup to finish */
+ debugs(14, 5, "ipcacheParse: CNAME " << answers[k].rdata << " has no IPs! Recursing.");
+ ipcache_nbgethostbyname(answers[k].rdata, ipcacheHandleCnameRecurse, new generic_cbdata(i) );
+ i->cname_wait++;
+ }
+#endif /* DNS_CNAME */
+
+ continue;
+ }
+
+ // otherwise its an unknown RR. debug at level 9 since we usually want to ignore these and they are common.
+ debugs(14, 9, HERE << "Unknown RR type received: type=" << answers[k].type << " starting at " << &(answers[k]) );
+ }
+
+#if DNS_CNAME
+ if (na == 0 && i->cname_wait >0 ) {
+ /* don't set any error message (yet). Allow recursion to do its work first. */
+ IpcacheStats.cname_only++;
+ return 0;
}
+#endif /* DNS_CNAME */
+
if (na == 0) {
- debug(14, 1) ("ipcacheParse: No Address records\n");
- i.error_message = xstrdup("No Address records");
- return &i;
- }
- i.flags.negcached = 0;
- i.addrs.in_addrs = xcalloc(na, sizeof(struct in_addr));
- i.addrs.bad_mask = xcalloc(na, sizeof(unsigned char));
- i.addrs.count = (unsigned char) na;
+ debugs(14, 1, "ipcacheParse: No Address records in response to '" << name << "'");
+ i->error_message = xstrdup("No Address records");
+ if (cname_found)
+ IpcacheStats.cname_only++;
+ return 0;
+ }
+
+ i->addrs.in_addrs = (IpAddress *)xcalloc(na, sizeof(IpAddress));
+ for (int l = 0; l < na; l++)
+ i->addrs.in_addrs[l].SetEmpty(); // perform same init actions as constructor would.
+ i->addrs.bad_mask = (unsigned char *)xcalloc(na, sizeof(unsigned char));
+
for (j = 0, k = 0; k < nr; k++) {
- if (answers[k].type != RFC1035_TYPE_A)
- continue;
- if (answers[k].class != RFC1035_CLASS_IN)
- continue;
- if (j == 0)
- i.expires = squid_curtime + answers[k].ttl;
- assert(answers[k].rdlength == 4);
- xmemcpy(&i.addrs.in_addrs[j++], answers[k].rdata, 4);
- debug(14, 3) ("ipcacheParse: #%d %s\n",
- j - 1,
- inet_ntoa(i.addrs.in_addrs[j - 1]));
+
+ if (answers[k].type == RFC1035_TYPE_A) {
+ if (answers[k].rdlength != sizeof(struct in_addr))
+ continue;
+
+ struct in_addr temp;
+ xmemcpy(&temp, answers[k].rdata, sizeof(struct in_addr));
+ i->addrs.in_addrs[j] = temp;
+
+ debugs(14, 3, "ipcacheParse: " << name << " #" << j << " " << i->addrs.in_addrs[j]);
+ j++;
+
+#if USE_IPV6
+ } else if (answers[k].type == RFC1035_TYPE_AAAA) {
+ if (answers[k].rdlength != sizeof(struct in6_addr))
+ continue;
+
+ struct in6_addr temp;
+ xmemcpy(&temp, answers[k].rdata, sizeof(struct in6_addr));
+ i->addrs.in_addrs[j] = temp;
+
+ debugs(14, 3, "ipcacheParse: " << name << " #" << j << " " << i->addrs.in_addrs[j] );
+ j++;
+#endif
+ }
+#if DNS_CNAME
+ else if (answers[k].type == RFC1035_TYPE_CNAME) {
+ debugs(14, 3, "ipcacheParse: " << name << " #x CNAME " << answers[k].rdata);
+ const ipcache_addrs *res = ipcache_gethostbyname(answers[k].rdata, 0);
+ if (res) {
+ /* NP: the results of *that* query need to be integrated in place of the CNAME */
+ /* Ideally we should also integrate the min TTL of the above IPA's into ttl. */
+ for (int l = 0; l < res->count; l++, j++) {
+ i->addrs.in_addrs[j] = res->in_addrs[l];
+ debugs(14, 3, "ipcacheParse: " << name << " #" << j << " " << i->addrs.in_addrs[j] );
+ }
+ } else {
+ debugs(14, 9, "ipcacheParse: " << answers[k].rdata << " (CNAME) waiting on A/AAAA records.");
+ }
+ }
+#endif /* DNS_CNAME */
+
+ if (ttl == 0 || (int) answers[k].ttl < ttl)
+ ttl = answers[k].ttl;
}
+
assert(j == na);
- return &i;
+
+ if (na < 256)
+ i->addrs.count = (unsigned char) na;
+ else
+ i->addrs.count = 255;
+
+ if (ttl > Config.positiveDnsTtl)
+ ttl = Config.positiveDnsTtl;
+
+ if (ttl < Config.negativeDnsTtl)
+ ttl = Config.negativeDnsTtl;
+
+ i->expires = squid_curtime + ttl;
+
+ i->flags.negcached = 0;
+
+#if DNS_CNAME
+ /* SPECIAL CASE: may get here IFF CNAME received with Additional records */
+ /* reurn 0/'wait for further details' value. */
+ /* NP: 'No DNS Results' is a return -1 +msg */
+ if (i->cname_wait)
+ return 0;
+ else
+#endif /* DNS_CNAME */
+ return i->addrs.count;
}
+
#endif
+/// \ingroup IPCacheInternal
static void
#if USE_DNSSERVERS
ipcacheHandleReply(void *data, char *reply)
#else
-ipcacheHandleReply(void *data, rfc1035_rr * answers, int na)
+ipcacheHandleReply(void *data, rfc1035_rr * answers, int na, const char *error_message)
#endif
{
- int n;
- generic_cbdata *c = data;
- ipcache_entry *i = c->data;
- ipcache_entry *x = NULL;
- cbdataFree(c);
- c = NULL;
- n = ++IpcacheStats.replies;
- statHistCount(&statCounter.dns.svc_time,
- tvSubMsec(i->request_time, current_time));
+ int done;
+ ipcache_entry *i;
+ static_cast<generic_cbdata *>(data)->unwrap(&i);
+ IpcacheStats.replies++;
+ const int age = i->age();
+ statHistCount(&statCounter.dns.svc_time, age);
+
#if USE_DNSSERVERS
- x = ipcacheParse(reply);
+
+ done = ipcacheParse(i, reply);
#else
- x = ipcacheParse(answers, na);
+
+ done = ipcacheParse(i, answers, na, error_message);
+
+ /* If we have not produced either IPs or Error immediately, wait for recursion to finish. */
+ if (done != 0 || error_message != NULL)
#endif
- assert(x);
- i->addrs = x->addrs;
- i->error_message = x->error_message;
- i->expires = x->expires;
- i->flags = x->flags;
- ipcacheAddEntry(i);
- ipcacheCallback(i);
+
+ {
+ ipcacheAddEntry(i);
+ ipcacheCallback(i, age);
+ }
}
+/**
+ \ingroup IPCacheAPI
+ *
+ \param name Host to resolve.
+ \param handler Pointer to the function to be called when the reply
+ * from the IP cache (or the DNS if the IP cache misses)
+ \param handlerData Information that is passed to the handler and does not affect the IP cache.
+ *
+ * XXX: on hits and some errors, the handler is called immediately instead
+ * of scheduling an async call. This reentrant behavior means that the
+ * user job must be extra careful after calling ipcache_nbgethostbyname,
+ * especially if the handler destroys the job. Moreover, the job has
+ * no way of knowing whether the reentrant call happened. commConnectStart
+ * protects the job by scheduling an async call, but some user code calls
+ * ipcache_nbgethostbyname directly.
+ */
void
ipcache_nbgethostbyname(const char *name, IPH * handler, void *handlerData)
{
const ipcache_addrs *addrs = NULL;
generic_cbdata *c;
assert(handler != NULL);
- debug(14, 4) ("ipcache_nbgethostbyname: Name '%s'.\n", name);
+ debugs(14, 4, "ipcache_nbgethostbyname: Name '" << name << "'.");
IpcacheStats.requests++;
+
if (name == NULL || name[0] == '\0') {
- debug(14, 4) ("ipcache_nbgethostbyname: Invalid name!\n");
- handler(NULL, handlerData);
- return;
+ debugs(14, 4, "ipcache_nbgethostbyname: Invalid name!");
+ IpcacheStats.invalid++;
+ const DnsLookupDetails details("Invalid hostname", -1); // error, no lookup
+ handler(NULL, details, handlerData);
+ return;
}
+
if ((addrs = ipcacheCheckNumeric(name))) {
- handler(addrs, handlerData);
- return;
+ debugs(14, 4, "ipcache_nbgethostbyname: BYPASS for '" << name << "' (already numeric)");
+ IpcacheStats.numeric_hits++;
+ const DnsLookupDetails details(NULL, -1); // no error, no lookup
+ handler(addrs, details, handlerData);
+ return;
}
+
i = ipcache_get(name);
+
if (NULL == i) {
- /* miss */
- (void) 0;
+ /* miss */
+ (void) 0;
} else if (ipcacheExpiredEntry(i)) {
- /* hit, but expired -- bummer */
- ipcacheRelease(i);
- i = NULL;
+ /* hit, but expired -- bummer */
+ ipcacheRelease(i);
+ i = NULL;
} else {
- /* hit */
- debug(14, 4) ("ipcache_nbgethostbyname: HIT for '%s'\n", name);
- if (i->flags.negcached)
- IpcacheStats.negative_hits++;
- else
- IpcacheStats.hits++;
- i->handler = handler;
- i->handlerData = handlerData;
- cbdataLock(handlerData);
- ipcacheCallback(i);
- return;
- }
- debug(14, 5) ("ipcache_nbgethostbyname: MISS for '%s'\n", name);
+ /* hit */
+ debugs(14, 4, "ipcache_nbgethostbyname: HIT for '" << name << "'");
+
+ if (i->flags.negcached)
+ IpcacheStats.negative_hits++;
+ else
+ IpcacheStats.hits++;
+
+ i->handler = handler;
+
+ i->handlerData = cbdataReference(handlerData);
+
+ ipcacheCallback(i, -1); // no lookup
+
+ return;
+ }
+
+ debugs(14, 5, "ipcache_nbgethostbyname: MISS for '" << name << "'");
IpcacheStats.misses++;
i = ipcacheCreateEntry(name);
i->handler = handler;
- i->handlerData = handlerData;
- cbdataLock(handlerData);
+ i->handlerData = cbdataReference(handlerData);
i->request_time = current_time;
- c = memAllocate(MEM_GEN_CBDATA);
- c->data = i;
- cbdataAdd(c, memFree, MEM_GEN_CBDATA);
+ c = new generic_cbdata(i);
#if USE_DNSSERVERS
+
dnsSubmit(hashKeyStr(&i->hash), ipcacheHandleReply, c);
#else
+
idnsALookup(hashKeyStr(&i->hash), ipcacheHandleReply, c);
#endif
}
-/* initialize the ipcache */
+/// \ingroup IPCacheInternal
+static void
+ipcacheRegisterWithCacheManager(void)
+{
+ CacheManager::GetInstance()->
+ registerAction("ipcache",
+ "IP Cache Stats and Contents",
+ stat_ipcache_get, 0, 1);
+}
+
+
+/**
+ \ingroup IPCacheAPI
+ *
+ * Initialize the ipcache.
+ * Is called from mainInitialize() after disk initialization
+ * and prior to the reverse FQDNCache initialization
+ */
void
ipcache_init(void)
{
int n;
- debug(14, 3) ("Initializing IP Cache...\n");
+ debugs(14, DBG_IMPORTANT, "Initializing IP Cache...");
memset(&IpcacheStats, '\0', sizeof(IpcacheStats));
memset(&lru_list, '\0', sizeof(lru_list));
- /* test naming lookup */
- if (!opt_dns_tests) {
- debug(14, 4) ("ipcache_init: Skipping DNS name lookup tests.\n");
- } else if (!ipcache_testname()) {
- fatal("ipcache_init: DNS name lookup tests failed.");
- } else {
- debug(14, 1) ("Successful DNS name lookup tests...\n");
- }
memset(&static_addrs, '\0', sizeof(ipcache_addrs));
- static_addrs.in_addrs = xcalloc(1, sizeof(struct in_addr));
- static_addrs.bad_mask = xcalloc(1, sizeof(unsigned char));
+
+ static_addrs.in_addrs = (IpAddress *)xcalloc(1, sizeof(IpAddress));
+ static_addrs.in_addrs->SetEmpty(); // properly setup the IpAddress!
+ static_addrs.bad_mask = (unsigned char *)xcalloc(1, sizeof(unsigned char));
ipcache_high = (long) (((float) Config.ipcache.size *
- (float) Config.ipcache.high) / (float) 100);
+ (float) Config.ipcache.high) / (float) 100);
ipcache_low = (long) (((float) Config.ipcache.size *
- (float) Config.ipcache.low) / (float) 100);
+ (float) Config.ipcache.low) / (float) 100);
n = hashPrime(ipcache_high / 4);
ip_table = hash_create((HASHCMP *) strcmp, n, hash4);
- cachemgrRegister("ipcache",
- "IP Cache Stats and Contents",
- stat_ipcache_get, 0, 1);
memDataInit(MEM_IPCACHE_ENTRY, "ipcache_entry", sizeof(ipcache_entry), 0);
+
+ ipcacheRegisterWithCacheManager();
}
+/**
+ \ingroup IPCacheAPI
+ *
+ * Is different from ipcache_nbgethostbyname in that it only checks
+ * if an entry exists in the cache and does not by default contact the DNS,
+ * unless this is requested, by setting the flags.
+ *
+ \param name Host name to resolve.
+ \param flags Default is NULL, set to IP_LOOKUP_IF_MISS
+ * to explicitly perform DNS lookups.
+ *
+ \retval NULL An error occured during lookup
+ \retval NULL No results available in cache and no lookup specified
+ \retval * Pointer to the ipcahce_addrs structure containing the lookup results
+ */
const ipcache_addrs *
ipcache_gethostbyname(const char *name, int flags)
{
ipcache_entry *i = NULL;
ipcache_addrs *addrs;
assert(name);
- debug(14, 3) ("ipcache_gethostbyname: '%s', flags=%x\n", name, flags);
+ debugs(14, 3, "ipcache_gethostbyname: '" << name << "', flags=" << std::hex << flags);
IpcacheStats.requests++;
i = ipcache_get(name);
+
if (NULL == i) {
- (void) 0;
+ (void) 0;
} else if (ipcacheExpiredEntry(i)) {
- ipcacheRelease(i);
- i = NULL;
+ ipcacheRelease(i);
+ i = NULL;
} else if (i->flags.negcached) {
- IpcacheStats.negative_hits++;
- dns_error_message = i->error_message;
- return NULL;
+ IpcacheStats.negative_hits++;
+ // ignore i->error_message: the caller just checks IP cache presence
+ return NULL;
} else {
- IpcacheStats.hits++;
- i->lastref = squid_curtime;
- return &i->addrs;
+ IpcacheStats.hits++;
+ i->lastref = squid_curtime;
+ // ignore i->error_message: the caller just checks IP cache presence
+ return &i->addrs;
}
- if ((addrs = ipcacheCheckNumeric(name)))
- return addrs;
+
+ /* no entry [any more] */
+
+ if ((addrs = ipcacheCheckNumeric(name))) {
+ IpcacheStats.numeric_hits++;
+ return addrs;
+ }
+
IpcacheStats.misses++;
+
if (flags & IP_LOOKUP_IF_MISS)
- ipcache_nbgethostbyname(name, dummy_handler, NULL);
+ ipcache_nbgethostbyname(name, ipcacheHandleCnameRecurse, NULL);
+
return NULL;
}
+/// \ingroup IPCacheInternal
static void
ipcacheStatPrint(ipcache_entry * i, StoreEntry * sentry)
{
int k;
- storeAppendPrintf(sentry, " %-32.32s %c %6d %6d %2d(%2d)",
- hashKeyStr(&i->hash),
- i->flags.negcached ? 'N' : ' ',
- (int) (squid_curtime - i->lastref),
- (int) ((i->flags.fromhosts ? -1 : i->expires - squid_curtime)),
- (int) i->addrs.count,
- (int) i->addrs.badcount);
- for (k = 0; k < (int) i->addrs.count; k++) {
- storeAppendPrintf(sentry, " %15s-%3s", inet_ntoa(i->addrs.in_addrs[k]),
- i->addrs.bad_mask[k] ? "BAD" : "OK ");
+ char buf[MAX_IPSTRLEN];
+
+ if (!sentry) {
+ debugs(14, 0, HERE << "CRITICAL: sentry is NULL!");
+ return;
+ }
+
+ if (!i) {
+ debugs(14, 0, HERE << "CRITICAL: ipcache_entry is NULL!");
+ storeAppendPrintf(sentry, "CRITICAL ERROR\n");
+ return;
+ }
+
+ int count = i->addrs.count;
+
+ storeAppendPrintf(sentry, " %-32.32s %c%c %6d %6d %2d(%2d)",
+ hashKeyStr(&i->hash),
+ i->flags.fromhosts ? 'H' : ' ',
+ i->flags.negcached ? 'N' : ' ',
+ (int) (squid_curtime - i->lastref),
+ (int) ((i->flags.fromhosts ? -1 : i->expires - squid_curtime)),
+ (int) i->addrs.count,
+ (int) i->addrs.badcount);
+
+ /** \par
+ * Negative-cached entries have no IPs listed. */
+ if (i->flags.negcached) {
+ storeAppendPrintf(sentry, "\n");
+ return;
+ }
+
+ /** \par
+ * Cached entries have IPs listed with a BNF of: ip-address '-' ('OK'|'BAD') */
+ for (k = 0; k < count; k++) {
+ /* Display tidy-up: IPv6 are so big make the list vertical */
+ if (k == 0)
+ storeAppendPrintf(sentry, " %45.45s-%3s\n",
+ i->addrs.in_addrs[k].NtoA(buf,MAX_IPSTRLEN),
+ i->addrs.bad_mask[k] ? "BAD" : "OK ");
+ else
+ storeAppendPrintf(sentry, "%s %45.45s-%3s\n",
+ " ", /* blank-space indenting IP list */
+ i->addrs.in_addrs[k].NtoA(buf,MAX_IPSTRLEN),
+ i->addrs.bad_mask[k] ? "BAD" : "OK ");
}
- storeAppendPrintf(sentry, "\n");
}
-/* process objects list */
+/**
+ \ingroup IPCacheInternal
+ *
+ * process objects list
+ */
void
stat_ipcache_get(StoreEntry * sentry)
{
dlink_node *m;
assert(ip_table != NULL);
storeAppendPrintf(sentry, "IP Cache Statistics:\n");
- storeAppendPrintf(sentry, "IPcache Entries: %d\n",
- memInUse(MEM_IPCACHE_ENTRY));
+ storeAppendPrintf(sentry, "IPcache Entries: %d\n",
+ memInUse(MEM_IPCACHE_ENTRY));
storeAppendPrintf(sentry, "IPcache Requests: %d\n",
- IpcacheStats.requests);
- storeAppendPrintf(sentry, "IPcache Hits: %d\n",
- IpcacheStats.hits);
- storeAppendPrintf(sentry, "IPcache Negative Hits: %d\n",
- IpcacheStats.negative_hits);
- storeAppendPrintf(sentry, "IPcache Misses: %d\n",
- IpcacheStats.misses);
- storeAppendPrintf(sentry, "Blocking calls to gethostbyname(): %d\n",
- IpcacheStats.ghbn_calls);
- storeAppendPrintf(sentry, "Attempts to release locked entries: %d\n",
- IpcacheStats.release_locked);
+ IpcacheStats.requests);
+ storeAppendPrintf(sentry, "IPcache Hits: %d\n",
+ IpcacheStats.hits);
+ storeAppendPrintf(sentry, "IPcache Negative Hits: %d\n",
+ IpcacheStats.negative_hits);
+ storeAppendPrintf(sentry, "IPcache Numeric Hits: %d\n",
+ IpcacheStats.numeric_hits);
+ storeAppendPrintf(sentry, "IPcache Misses: %d\n",
+ IpcacheStats.misses);
+ storeAppendPrintf(sentry, "IPcache Retrieved A: %d\n",
+ IpcacheStats.rr_a);
+ storeAppendPrintf(sentry, "IPcache Retrieved AAAA: %d\n",
+ IpcacheStats.rr_aaaa);
+ storeAppendPrintf(sentry, "IPcache Retrieved CNAME: %d\n",
+ IpcacheStats.rr_cname);
+ storeAppendPrintf(sentry, "IPcache CNAME-Only Response: %d\n",
+ IpcacheStats.cname_only);
+ storeAppendPrintf(sentry, "IPcache Invalid Request: %d\n",
+ IpcacheStats.invalid);
storeAppendPrintf(sentry, "\n\n");
storeAppendPrintf(sentry, "IP Cache Contents:\n\n");
- storeAppendPrintf(sentry, " %-29.29s %3s %6s %6s %1s\n",
- "Hostname",
- "Flg",
- "lstref",
- "TTL",
- "N");
- for (m = lru_list.head; m; m = m->next)
- ipcacheStatPrint(m->data, sentry);
+ storeAppendPrintf(sentry, " %-31.31s %3s %6s %6s %4s\n",
+ "Hostname",
+ "Flg",
+ "lstref",
+ "TTL",
+ "N(b)");
+
+ for (m = lru_list.head; m; m = m->next) {
+ assert( m->next != m );
+ ipcacheStatPrint((ipcache_entry *)m->data, sentry);
+ }
+}
+
+#if DNS_CNAME
+/**
+ * Takes two IpAddress arrays and merges them into a single array
+ * which is allocated dynamically to fit the number of unique addresses
+ *
+ \param aaddrs One list to merge
+ \param alen Size of list aaddrs
+ \param baddrs Other list to merge
+ \param alen Size of list baddrs
+ \param out Combined list of unique addresses (sorted with IPv6 first in IPv6-mode)
+ \param outlen Size of list out
+ */
+void
+ipcacheMergeIPLists(const IpAddress *aaddrs, const int alen,
+ const IpAddress *baddrs, const int blen,
+ IpAddress **out, int &outlen )
+{
+ int fc=0, t=0, c=0;
+
+ IpAddress const *ip4ptrs[255];
+#if USE_IPV6
+ IpAddress const *ip6ptrs[255];
+#endif
+ int num_ip4 = 0;
+ int num_ip6 = 0;
+
+ memset(ip4ptrs, 0, sizeof(IpAddress*)*255);
+#if USE_IPV6
+ memset(ip6ptrs, 0, sizeof(IpAddress*)*255);
+#endif
+
+ // for each unique address in list A - grab ptr
+ for (t = 0; t < alen; t++) {
+ if (aaddrs[t].IsIPv4()) {
+ // check against IPv4 pruned list
+ for (c = 0; c <= num_ip4; c++) {
+ if (ip4ptrs[c] && aaddrs[t] == *(ip4ptrs[c]) ) break; // duplicate.
+ }
+ if (c > num_ip4) {
+ ip4ptrs[num_ip4] = &aaddrs[t];
+ num_ip4++;
+ }
+ }
+#if USE_IPV6
+ else if (aaddrs[t].IsIPv6()) {
+ debugs(14,8, HERE << "A[" << t << "]=IPv6 " << aaddrs[t]);
+ // check against IPv6 pruned list
+ for (c = 0; c <= num_ip6; c++) {
+ if (ip6ptrs[c] && aaddrs[t] == *ip6ptrs[c]) break; // duplicate.
+ }
+ if (c > num_ip6) {
+ ip6ptrs[num_ip6] = &aaddrs[t];
+ num_ip6++;
+ }
+ }
+#endif
+ }
+
+ // for each unique address in list B - grab ptr
+ for (t = 0; t < blen; t++) {
+ if (baddrs[t].IsIPv4()) {
+ // check against IPv4 pruned list
+ for (c = 0; c <= num_ip4; c++) {
+ if (ip4ptrs[c] && baddrs[t] == *ip4ptrs[c]) break; // duplicate.
+ }
+ if (c > num_ip4) {
+ ip4ptrs[num_ip4] = &baddrs[t];
+ num_ip4++;
+ }
+ }
+#if USE_IPV6
+ else if (baddrs[t].IsIPv6()) {
+ // check against IPv6 pruned list
+ for (c = 0; c <= num_ip6; c++) {
+ if (ip6ptrs[c] && baddrs[t] == *ip6ptrs[c]) break; // duplicate.
+ }
+ if (c > num_ip6) {
+ ip6ptrs[num_ip6] = &baddrs[t];
+ num_ip6++;
+ }
+ }
+#endif
+ }
+
+ fc = num_ip6 + num_ip4;
+
+ assert(fc > 0);
+
+ debugs(14, 5, "ipcacheMergeIPLists: Merge " << alen << "+" << blen << " into " << fc << " unique IPs.");
+
+ // copy the old IPs into the new list buffer.
+ (*out) = (IpAddress*)xcalloc(fc, sizeof(IpAddress));
+ outlen=0;
+
+ assert(out != NULL);
+
+#if USE_IPV6
+ /* IPv6 are preferred (tried first) over IPv4 */
+
+ for (int l = 0; outlen < num_ip6; l++, outlen++) {
+ (*out)[outlen] = *ip6ptrs[l];
+ debugs(14, 5, "ipcacheMergeIPLists: #" << outlen << " " << (*out)[outlen] );
+ }
+#endif /* USE_IPV6 */
+
+ for (int l = 0; outlen < num_ip4; l++, outlen++) {
+ (*out)[outlen] = *ip4ptrs[l];
+ debugs(14, 5, "ipcacheMergeIPLists: #" << outlen << " " << (*out)[outlen] );
+ }
+
+ assert(outlen == fc); // otherwise something broke badly!
}
+#endif /* DNS_CNAME */
+/// \ingroup IPCacheInternal
+/// Callback.
static void
-dummy_handler(const ipcache_addrs * addrsnotused, void *datanotused)
+ipcacheHandleCnameRecurse(const ipcache_addrs *addrs, const DnsLookupDetails &, void *cbdata)
{
- return;
+#if DNS_CNAME
+ ipcache_entry *i = NULL;
+ char *pname = NULL;
+ IpAddress *tmpbuf = NULL;
+ int fc = 0;
+ int ttl = 0;
+ generic_cbdata* gcb = (generic_cbdata*)cbdata;
+ // count of addrs at parent and child (REQ as .count is a char type!)
+ int ccount = 0, pcount = 0;
+
+ debugs(14, 5, "ipcacheHandleCnameRecurse: Handling basic A/AAAA response.");
+
+ /* IFF no CNAME recursion being processed. do nothing. */
+ if (cbdata == NULL)
+ return;
+
+ gcb->unwrap(&i);
+ assert(i != NULL);
+
+ // make sure we are actualy waiting for a CNAME callback to be run.
+ assert(i->cname_wait > 0);
+ // count this event. its being handled.
+ i->cname_wait--;
+
+ pname = (char*)i->hash.key;
+ assert(pname != NULL);
+
+ debugs(14, 5, "ipcacheHandleCnameRecurse: Handling CNAME recursion. CBDATA('" << gcb->data << "')='" << pname << "' -> " << std::hex << i);
+
+ if (i == NULL) {
+ return; // Parent has expired. Don't merge, just leave for future Ref:
+ }
+
+ /* IFF addrs is NULL (Usually an Error or Timeout occured on lookup.) */
+ /* Ignore it and HOPE that we got some Additional records to use. */
+ if (addrs == NULL)
+ return;
+
+ ccount = (0+ addrs->count);
+ pcount = (0+ i->addrs.count);
+ ttl = i->expires;
+
+ /* IFF no CNAME results. do none of the processing BUT finish anyway. */
+ if (addrs) {
+
+ debugs(14, 5, "ipcacheHandleCnameRecurse: Merge IP Lists for " << pname << " (" << pcount << "+" << ccount << ")");
+
+ /* add new IP records to entry */
+ tmpbuf = i->addrs.in_addrs;
+ i->addrs.in_addrs = NULL;
+ ipcacheMergeIPLists(tmpbuf, pcount, addrs->in_addrs, ccount, &(i->addrs.in_addrs), fc);
+ debugs(14,8, HERE << "in=" << tmpbuf << ", out=" << i->addrs.in_addrs );
+ assert( (pcount>0 ? tmpbuf!=NULL : tmpbuf==NULL) );
+ safe_free(tmpbuf);
+
+ if ( pcount > 0) {
+ /* IFF the parent initial lookup was given Additional records with A */
+ // clear the 'bad IP mask'
+ safe_free(i->addrs.bad_mask);
+ }
+ // create a new bad IP mask to fit the new size needed.
+ if (fc > 0) {
+ i->addrs.bad_mask = (unsigned char*)xcalloc(fc, sizeof(unsigned char));
+ memset(i->addrs.bad_mask, 0, sizeof(unsigned char)*fc);
+ }
+
+ if (fc < 256)
+ i->addrs.count = (unsigned char) fc;
+ else
+ i->addrs.count = 255;
+
+ if (ttl == 0 || ttl > Config.positiveDnsTtl)
+ ttl = Config.positiveDnsTtl;
+
+ if (ttl < Config.negativeDnsTtl)
+ ttl = Config.negativeDnsTtl;
+
+ i->expires = squid_curtime + ttl;
+
+ i->flags.negcached = 0;
+
+ i->addrs.cur = 0;
+
+ i->addrs.badcount = 0;
+ }
+
+ if (fc == 0) {
+ i->error_message = xstrdup("No DNS Records");
+ }
+
+ /* finish the lookup we were doing on parent when we got side-tracked for CNAME loop */
+ if (i->cname_wait == 0) {
+ ipcacheAddEntry(i);
+ ipcacheCallback(i, i->age()); // age since i creation, includes CNAMEs
+ }
+ // else still more CNAME to be found.
+#endif /* DNS_CNAME */
}
+/// \ingroup IPCacheAPI
void
ipcacheInvalidate(const char *name)
{
ipcache_entry *i;
+
if ((i = ipcache_get(name)) == NULL)
- return;
+ return;
+
i->expires = squid_curtime;
+
/*
- * NOTE, don't call ipcacheRelease here becuase we might be here due
+ * NOTE, don't call ipcacheRelease here because we might be here due
* to a thread started from a callback.
*/
}
+/// \ingroup IPCacheAPI
+void
+ipcacheInvalidateNegative(const char *name)
+{
+ ipcache_entry *i;
+
+ if ((i = ipcache_get(name)) == NULL)
+ return;
+
+ if (i->flags.negcached)
+ i->expires = squid_curtime;
+
+ /*
+ * NOTE, don't call ipcacheRelease here because we might be here due
+ * to a thread started from a callback.
+ */
+}
+
+/// \ingroup IPCacheAPI
ipcache_addrs *
ipcacheCheckNumeric(const char *name)
{
- struct in_addr ip;
+
+ IpAddress ip;
/* check if it's already a IP address in text form. */
- if (!safe_inet_addr(name, &ip))
- return NULL;
+
+ /* it may be IPv6-wrapped */
+ if (name[0] == '[') {
+ char *tmp = xstrdup(&name[1]);
+ tmp[strlen(tmp)-1] = '\0';
+ if (!(ip = tmp)) {
+ delete tmp;
+ return NULL;
+ }
+ delete tmp;
+ } else if (!(ip = name))
+ return NULL;
+
+ debugs(14, 4, "ipcacheCheckNumeric: HIT_BYPASS for '" << name << "' == " << ip );
+
static_addrs.count = 1;
+
static_addrs.cur = 0;
- static_addrs.in_addrs[0].s_addr = ip.s_addr;
+
+ static_addrs.in_addrs[0] = ip;
+
static_addrs.bad_mask[0] = FALSE;
+
static_addrs.badcount = 0;
+
return &static_addrs;
}
+/// \ingroup IPCacheInternal
static void
ipcacheLockEntry(ipcache_entry * i)
{
if (i->locks++ == 0) {
- dlinkDelete(&i->lru, &lru_list);
- dlinkAdd(i, &i->lru, &lru_list);
+ dlinkDelete(&i->lru, &lru_list);
+ dlinkAdd(i, &i->lru, &lru_list);
}
}
+/// \ingroup IPCacheInternal
static void
ipcacheUnlockEntry(ipcache_entry * i)
{
- assert(i->locks > 0);
+ if (i->locks < 1) {
+ debugs(14, 1, "WARNING: ipcacheEntry unlocked with no lock! locks=" << i->locks);
+ return;
+ }
+
i->locks--;
+
if (ipcacheExpiredEntry(i))
- ipcacheRelease(i);
+ ipcacheRelease(i);
}
+/// \ingroup IPCacheAPI
void
ipcacheCycleAddr(const char *name, ipcache_addrs * ia)
{
ipcache_entry *i;
unsigned char k;
assert(name || ia);
+
if (NULL == ia) {
- if ((i = ipcache_get(name)) == NULL)
- return;
- if (i->flags.negcached)
- return;
- ia = &i->addrs;
+ if ((i = ipcache_get(name)) == NULL)
+ return;
+
+ if (i->flags.negcached)
+ return;
+
+ ia = &i->addrs;
}
+
for (k = 0; k < ia->count; k++) {
- if (++ia->cur == ia->count)
- ia->cur = 0;
- if (!ia->bad_mask[ia->cur])
- break;
+ if (++ia->cur == ia->count)
+ ia->cur = 0;
+
+ if (!ia->bad_mask[ia->cur])
+ break;
}
+
if (k == ia->count) {
- /* All bad, reset to All good */
- debug(14, 3) ("ipcacheCycleAddr: Changing ALL %s addrs from BAD to OK\n",
- name);
- for (k = 0; k < ia->count; k++)
- ia->bad_mask[k] = 0;
- ia->badcount = 0;
- ia->cur = 0;
+ /* All bad, reset to All good */
+ debugs(14, 3, "ipcacheCycleAddr: Changing ALL " << name << " addrs from BAD to OK");
+
+ for (k = 0; k < ia->count; k++)
+ ia->bad_mask[k] = 0;
+
+ ia->badcount = 0;
+
+ ia->cur = 0;
}
- debug(14, 3) ("ipcacheCycleAddr: %s now at %s\n", name,
- inet_ntoa(ia->in_addrs[ia->cur]));
+
+ debugs(14, 3, "ipcacheCycleAddr: " << name << " now at " << ia->in_addrs[ia->cur] << " (" << ia->cur << " of " << ia->count << ")");
}
-/*
- * Marks the given address as BAD and calls ipcacheCycleAddr to
- * advance the current pointer to the next OK address.
+/**
+ \ingroup IPCacheAPI
+ *
+ \param name domain name to have an IP marked bad
+ \param addr specific addres to be marked bad
*/
void
-ipcacheMarkBadAddr(const char *name, struct in_addr addr)
+ipcacheMarkBadAddr(const char *name, IpAddress &addr)
{
ipcache_entry *i;
ipcache_addrs *ia;
int k;
+
+ /** Does nothing if the domain name does not exist. */
if ((i = ipcache_get(name)) == NULL)
- return;
+ return;
+
ia = &i->addrs;
+
for (k = 0; k < (int) ia->count; k++) {
- if (ia->in_addrs[k].s_addr == addr.s_addr)
- break;
+ if (addr == ia->in_addrs[k] )
+ break;
}
- if (k == (int) ia->count) /* not found */
- return;
+
+ /** Does nothing if the IP does not exist for the doamin. */
+ if (k == (int) ia->count)
+ return;
+
+ /** Marks the given address as BAD */
if (!ia->bad_mask[k]) {
- ia->bad_mask[k] = TRUE;
- ia->badcount++;
- debug(14, 2) ("ipcacheMarkBadAddr: %s [%s]\n", name, inet_ntoa(addr));
+ ia->bad_mask[k] = TRUE;
+ ia->badcount++;
+ i->expires = min(squid_curtime + max((time_t)60, Config.negativeDnsTtl), i->expires);
+ debugs(14, 2, "ipcacheMarkBadAddr: " << name << " " << addr );
}
+
+ /** then calls ipcacheCycleAddr() to advance the current pointer to the next OK address. */
ipcacheCycleAddr(name, ia);
}
+/// \ingroup IPCacheAPI
void
-ipcacheMarkGoodAddr(const char *name, struct in_addr addr)
+ipcacheMarkGoodAddr(const char *name, IpAddress &addr)
{
ipcache_entry *i;
ipcache_addrs *ia;
int k;
+
if ((i = ipcache_get(name)) == NULL)
- return;
+ return;
+
ia = &i->addrs;
+
for (k = 0; k < (int) ia->count; k++) {
- if (ia->in_addrs[k].s_addr == addr.s_addr)
- break;
+ if (addr == ia->in_addrs[k])
+ break;
}
+
if (k == (int) ia->count) /* not found */
- return;
+ return;
+
if (!ia->bad_mask[k]) /* already OK */
- return;
+ return;
+
ia->bad_mask[k] = FALSE;
+
ia->badcount--;
- debug(14, 2) ("ipcacheMarkGoodAddr: %s [%s]\n", name, inet_ntoa(addr));
+
+ debugs(14, 2, "ipcacheMarkGoodAddr: " << name << " " << addr );
}
+/// \ingroup IPCacheInternal
static void
ipcacheFreeEntry(void *data)
{
- ipcache_entry *i = data;
+ ipcache_entry *i = (ipcache_entry *)data;
safe_free(i->addrs.in_addrs);
safe_free(i->addrs.bad_mask);
safe_free(i->hash.key);
memFree(i, MEM_IPCACHE_ENTRY);
}
+/// \ingroup IPCacheAPI
void
ipcacheFreeMemory(void)
{
ip_table = NULL;
}
-/* Recalculate IP cache size upon reconfigure */
+/**
+ \ingroup IPCacheAPI
+ *
+ * Recalculate IP cache size upon reconfigure.
+ * Is called to clear the IPCache's data structures,
+ * cancel all pending requests.
+ */
void
ipcache_restart(void)
{
ipcache_high = (long) (((float) Config.ipcache.size *
- (float) Config.ipcache.high) / (float) 100);
+ (float) Config.ipcache.high) / (float) 100);
ipcache_low = (long) (((float) Config.ipcache.size *
- (float) Config.ipcache.low) / (float) 100);
+ (float) Config.ipcache.low) / (float) 100);
purge_entries_fromhosts();
}
-/*
- * adds a "static" entry from /etc/hosts. the worldist is to be
- * managed by the caller, including pointed-to strings
+/**
+ \ingroup IPCacheAPI
+ *
+ * Adds a "static" entry from /etc/hosts
+ *
+ \param name Hostname to be linked with IP
+ \param ipaddr IP Address to be cached.
+ *
+ \retval 0 Success.
+ \retval 1 IP address is invalid or other error.
*/
int
ipcacheAddEntryFromHosts(const char *name, const char *ipaddr)
{
ipcache_entry *i;
- struct in_addr ip;
- if (!safe_inet_addr(ipaddr, &ip)) {
- debug(14, 1) ("ipcacheAddEntryFromHosts: bad IP address '%s'\n",
- ipaddr);
- return 1;
+
+ IpAddress ip;
+
+ if (!(ip = ipaddr)) {
+#if USE_IPV6
+ if (strchr(ipaddr, ':') && strspn(ipaddr, "0123456789abcdefABCDEF:") == strlen(ipaddr)) {
+ debugs(14, 3, "ipcacheAddEntryFromHosts: Skipping IPv6 address '" << ipaddr << "'");
+ } else {
+ debugs(14, 1, "ipcacheAddEntryFromHosts: Bad IP address '" << ipaddr << "'");
+ }
+#else
+ debugs(14, 1, "ipcacheAddEntryFromHosts: Bad IP address '" << ipaddr << "'");
+#endif
+
+ return 1;
}
+
if ((i = ipcache_get(name))) {
- if (1 == i->flags.fromhosts) {
- ipcacheUnlockEntry(i);
- } else if (i->locks > 0) {
- debug(35, 1) ("ipcacheAddEntryFromHosts: can't add static entry"
- " for locked name '%s'\n", name);
- return 1;
- } else {
- ipcacheRelease(i);
- }
+ if (1 == i->flags.fromhosts) {
+ ipcacheUnlockEntry(i);
+ } else if (i->locks > 0) {
+ debugs(14, 1, "ipcacheAddEntryFromHosts: can't add static entry for locked name '" << name << "'");
+ return 1;
+ } else {
+ ipcacheRelease(i);
+ }
}
+
i = ipcacheCreateEntry(name);
i->addrs.count = 1;
i->addrs.cur = 0;
i->addrs.badcount = 0;
- i->addrs.in_addrs = xcalloc(1, sizeof(struct in_addr));
- i->addrs.bad_mask = xcalloc(1, sizeof(unsigned char));
- i->addrs.in_addrs[0].s_addr = ip.s_addr;
+
+ i->addrs.in_addrs = (IpAddress *)xcalloc(1, sizeof(IpAddress));
+ i->addrs.bad_mask = (unsigned char *)xcalloc(1, sizeof(unsigned char));
+ i->addrs.in_addrs[0] = ip;
i->addrs.bad_mask[0] = FALSE;
i->flags.fromhosts = 1;
ipcacheAddEntry(i);
}
#ifdef SQUID_SNMP
-/*
+/**
+ \ingroup IPCacheAPI
+ *
* The function to return the ip cache statistics to via SNMP
*/
-
variable_list *
snmp_netIpFn(variable_list * Var, snint * ErrP)
{
variable_list *Answer = NULL;
- debug(49, 5) ("snmp_netIpFn: Processing request:\n", Var->name[LEN_SQ_NET + 1]);
+ debugs(49, 5, "snmp_netIpFn: Processing request:");
snmpDebugOid(5, Var->name, Var->name_length);
*ErrP = SNMP_ERR_NOERROR;
+
switch (Var->name[LEN_SQ_NET + 1]) {
+
case IP_ENT:
- Answer = snmp_var_new_integer(Var->name, Var->name_length,
- memInUse(MEM_IPCACHE_ENTRY),
- SMI_GAUGE32);
- break;
+ Answer = snmp_var_new_integer(Var->name, Var->name_length,
+ memInUse(MEM_IPCACHE_ENTRY),
+ SMI_GAUGE32);
+ break;
+
case IP_REQ:
- Answer = snmp_var_new_integer(Var->name, Var->name_length,
- IpcacheStats.requests,
- SMI_COUNTER32);
- break;
+ Answer = snmp_var_new_integer(Var->name, Var->name_length,
+ IpcacheStats.requests,
+ SMI_COUNTER32);
+ break;
+
case IP_HITS:
- Answer = snmp_var_new_integer(Var->name, Var->name_length,
- IpcacheStats.hits,
- SMI_COUNTER32);
- break;
+ Answer = snmp_var_new_integer(Var->name, Var->name_length,
+ IpcacheStats.hits,
+ SMI_COUNTER32);
+ break;
+
case IP_PENDHIT:
- Answer = snmp_var_new_integer(Var->name, Var->name_length,
- 0,
- SMI_GAUGE32);
- break;
+ Answer = snmp_var_new_integer(Var->name, Var->name_length,
+ 0,
+ SMI_GAUGE32);
+ break;
+
case IP_NEGHIT:
- Answer = snmp_var_new_integer(Var->name, Var->name_length,
- IpcacheStats.negative_hits,
- SMI_COUNTER32);
- break;
+ Answer = snmp_var_new_integer(Var->name, Var->name_length,
+ IpcacheStats.negative_hits,
+ SMI_COUNTER32);
+ break;
+
case IP_MISS:
- Answer = snmp_var_new_integer(Var->name, Var->name_length,
- IpcacheStats.misses,
- SMI_COUNTER32);
- break;
+ Answer = snmp_var_new_integer(Var->name, Var->name_length,
+ IpcacheStats.misses,
+ SMI_COUNTER32);
+ break;
+
case IP_GHBN:
- Answer = snmp_var_new_integer(Var->name, Var->name_length,
- IpcacheStats.ghbn_calls,
- SMI_COUNTER32);
- break;
+ Answer = snmp_var_new_integer(Var->name, Var->name_length,
+ 0, /* deprecated */
+ SMI_COUNTER32);
+ break;
+
case IP_LOC:
- Answer = snmp_var_new_integer(Var->name, Var->name_length,
- IpcacheStats.release_locked,
- SMI_COUNTER32);
- break;
+ Answer = snmp_var_new_integer(Var->name, Var->name_length,
+ 0, /* deprecated */
+ SMI_COUNTER32);
+ break;
+
default:
- *ErrP = SNMP_ERR_NOSUCHNAME;
- snmp_var_free(Answer);
- return (NULL);
+ *ErrP = SNMP_ERR_NOSUCHNAME;
+ snmp_var_free(Answer);
+ return (NULL);
}
+
return Answer;
}