From: Henrik Nordstrom Date: Mon, 10 May 2010 14:53:23 +0000 (+0200) Subject: Revert revision 10471 due to mixed commit. Proper commit in a few seconds X-Git-Tag: SQUID_3_2_0_1~224 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=5b8aff6bde0a46b705d0bb40c8e5e27ba5f09748;p=thirdparty%2Fsquid.git Revert revision 10471 due to mixed commit. Proper commit in a few seconds --- diff --git a/lib/tests/testRFC1035.cc b/lib/tests/testRFC1035.cc index 9af9df97c7..d716e9c4d5 100644 --- a/lib/tests/testRFC1035.cc +++ b/lib/tests/testRFC1035.cc @@ -132,6 +132,6 @@ void testRFC1035::testBugPacketHeadersOnly() res = rfc1035MessageUnpack(buf, len, &msg); CPPUNIT_ASSERT(0 == memcmp("The DNS reply message is corrupt or could not be safely parsed.", rfc1035ErrorMessage(res), 63)); - CPPUNIT_ASSERT(res < 0); + CPPUNIT_ASSERT(res == 0); CPPUNIT_ASSERT(msg == NULL); } diff --git a/src/ipcache.cc b/src/ipcache.cc index 91cec2f206..bfc67a8568 100644 --- a/src/ipcache.cc +++ b/src/ipcache.cc @@ -99,6 +99,10 @@ public: 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; @@ -131,6 +135,7 @@ static HLPCB ipcacheHandleReply; #else static IDNSCB ipcacheHandleReply; #endif +static IPH ipcacheHandleCnameRecurse; static int ipcacheExpiredEntry(ipcache_entry *); #if USE_DNSSERVERS static int ipcacheParse(ipcache_entry *, const char *buf); @@ -299,10 +304,22 @@ ipcacheAddEntry(ipcache_entry * i) { 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); +#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); @@ -506,12 +523,36 @@ ipcacheParse(ipcache_entry *i, rfc1035_rr * answers, int nr, const char *error_m 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) { debugs(14, 1, "ipcacheParse: No Address records in response to '" << name << "'"); i->error_message = xstrdup("No Address records"); @@ -551,6 +592,23 @@ ipcacheParse(ipcache_entry *i, rfc1035_rr * answers, int nr, const char *error_m 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; } @@ -572,7 +630,15 @@ ipcacheParse(ipcache_entry *i, rfc1035_rr * answers, int nr, const char *error_m i->flags.negcached = 0; - return i->addrs.count; +#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 @@ -786,7 +852,7 @@ ipcache_gethostbyname(const char *name, int flags) IpcacheStats.misses++; if (flags & IP_LOOKUP_IF_MISS) - ipcache_nbgethostbyname(name, NULL, NULL); + ipcache_nbgethostbyname(name, ipcacheHandleCnameRecurse, NULL); return NULL; } @@ -891,6 +957,224 @@ stat_ipcache_get(StoreEntry * sentry) } } +#if DNS_CNAME +/** + * Takes two Ip::Address 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 Ip::Address *aaddrs, const int alen, + const Ip::Address *baddrs, const int blen, + Ip::Address **out, int &outlen ) +{ + int fc=0, t=0, c=0; + + Ip::Address const *ip4ptrs[255]; +#if USE_IPV6 + Ip::Address const *ip6ptrs[255]; +#endif + int num_ip4 = 0; + int num_ip6 = 0; + + memset(ip4ptrs, 0, sizeof(Ip::Address*)*255); +#if USE_IPV6 + memset(ip6ptrs, 0, sizeof(Ip::Address*)*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) = static_cast(xcalloc(fc, sizeof(Ip::Address))); + 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 +ipcacheHandleCnameRecurse(const ipcache_addrs *addrs, const DnsLookupDetails &, void *cbdata) +{ +#if DNS_CNAME + ipcache_entry *i = NULL; + char *pname = NULL; + Ip::Address *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)