Skip to content

Commit

Permalink
fix SOA caching with multiple backends
Browse files Browse the repository at this point in the history
  • Loading branch information
mind04 committed Apr 27, 2016
1 parent 03281d9 commit 58d6ab6
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 105 deletions.
31 changes: 2 additions & 29 deletions pdns/dnsbackend.cc
Expand Up @@ -44,36 +44,9 @@ bool DNSBackend::getRemote(DNSPacket *p, struct sockaddr *sa, Utility::socklen_t
return true;
}

bool DNSBackend::getAuth(DNSPacket *p, SOAData *sd, const string &target, int *zoneId, const int best_match_len, map<string,int>& negCacheMap)
bool DNSBackend::getAuth(DNSPacket *p, SOAData *sd, const string &target)
{
bool found=false;
string subdomain(target);
do {
if( best_match_len >= (int)subdomain.length() && p->qtype != QType::DS )
break;

map<string,int>::iterator it = negCacheMap.find(subdomain);
bool negCached = ( it != negCacheMap.end() && it->second == 1 );

if(! negCached && this->getSOA( subdomain, *sd, p ) ) {
sd->qname = subdomain;
if(zoneId)
*zoneId = sd->domain_id;
if(found) // Second SOA found, we are done
return true;

if(p->qtype.getCode() == QType::DS && pdns_iequals(subdomain, target)) {
// Found authoritative zone but look for parent zone with 'DS' record.
found=true;
} else
return true;
}
if (found)
negCacheMap[subdomain]=2; // don't cache SOA's during our quest for a parent zone
}
while( chopOff( subdomain ) ); // 'www.powerdns.org' -> 'powerdns.org' -> 'org' -> ''

return found;
return this->getSOA(target, *sd, p);
}

void DNSBackend::setArgPrefix(const string &prefix)
Expand Down
2 changes: 1 addition & 1 deletion pdns/dnsbackend.hh
Expand Up @@ -163,7 +163,7 @@ public:
virtual void getAllDomains(vector<DomainInfo> *domains, bool include_disabled=false) { }

/** Determines if we are authoritative for a zone, and at what level */
virtual bool getAuth(DNSPacket *p, SOAData *sd, const string &target, int *zoneId, const int best_match_len, map<string,int>& negCacheMap);
virtual bool getAuth(DNSPacket *p, SOAData *sd, const string &target);

struct KeyData {
unsigned int id;
Expand Down
149 changes: 74 additions & 75 deletions pdns/ueberbackend.cc
Expand Up @@ -276,85 +276,73 @@ void UeberBackend::getUpdatedMasters(vector<DomainInfo>* domains)

bool UeberBackend::getAuth(DNSPacket *p, SOAData *sd, const string &target, int *zoneId)
{
int best_match_len = -1;
bool from_cache = false; // Was this result fetched from the cache?
map<string,int> negCacheMap;

// If not special case of caching explicitly disabled (sd->db = -1), first
// find the best match from the cache. If DS then we need to find parent so
// dont bother with caching as it confuses matters.
if( sd->db != (DNSBackend *)-1 && (d_cache_ttl || d_negcache_ttl)) {
string subdomain(target);
int cstat, loops = 0;
do {
d_question.qtype = QType::SOA;
d_question.qname = subdomain;
d_question.zoneId = -1;

cstat = cacheHas(d_question,d_answers);

if(cstat==1 && !d_answers.empty() && d_cache_ttl) {
fillSOAData(d_answers[0].content,*sd);
sd->domain_id = d_answers[0].domain_id;
sd->ttl = d_answers[0].ttl;
sd->db = 0;
sd->qname = subdomain;
//L<<Logger::Error<<"Best cache match: " << sd->qname << " itteration " << loops <<endl;

// Found first time round this must be the best match
if( loops == 0 && p->qtype != QType::DS)
return true;

from_cache = true;
best_match_len = sd->qname.length();

if ( p->qtype != QType::DS || best_match_len < (int)target.length())
break;
} else if (cstat==0 && d_negcache_ttl) {
negCacheMap[subdomain]=1;
} else
negCacheMap[subdomain]=0;
loops++;
bool found = false;
int cstat;
string shorter(target);
vector<pair<size_t, SOAData> > bestmatch (backends.size(), make_pair(target.size()+1, SOAData()));
do {

// Check cache
if(sd->db != (DNSBackend *)-1 && (d_cache_ttl || d_negcache_ttl)) {
d_question.qtype = QType::SOA;
d_question.qname = shorter;
d_question.zoneId = -1;

cstat = cacheHas(d_question,d_answers);

if(cstat == 1 && !d_answers.empty() && d_cache_ttl) {
DLOG(L<<Logger::Error<<"has pos: "<<shorter<<endl);
fillSOAData(d_answers[0].content, *sd);
sd->domain_id = d_answers[0].domain_id;
sd->ttl = d_answers[0].ttl;
sd->db = 0;
sd->qname = shorter;
goto found;
} else if(cstat == 0 && d_negcache_ttl) {
DLOG(L<<Logger::Error<<"has neg: "<<shorter<<endl);
continue;
}
while( chopOff( subdomain ) ); // 'www.powerdns.org' -> 'powerdns.org' -> 'org' -> ''
}

for(vector<DNSBackend *>::const_iterator i=backends.begin(); i!=backends.end();++i) {

// Shortcut for the case that we got a direct hit - no need to go
// through the other backends then.
if( best_match_len == (int)target.length() && p->qtype != QType::DS )
goto auth_found;

if((*i)->getAuth(p, sd, target, zoneId, best_match_len, negCacheMap)) {
best_match_len = sd->qname.length();
from_cache = false;
}
}

if( sd->db != (DNSBackend *)-1 && d_negcache_ttl) {
string shorter(target);

d_question.qtype=QType::SOA;
d_question.zoneId=-1;
while((int)shorter.length() > best_match_len ) {
map<string,int>::iterator it = negCacheMap.find(shorter);
if (it == negCacheMap.end() || it->second == 0) {
d_question.qname=shorter;
addNegCache(d_question);
// Check backends
{
vector<DNSBackend *>::const_iterator i = backends.begin();
vector<pair<size_t, SOAData> >::iterator j = bestmatch.begin();
for(; i != backends.end() && j != bestmatch.end(); ++i, ++j) {

L<<Logger::Error<<"backend: "<<i-backends.begin()<<", qname: "<<shorter<<endl;

if(j->first < shorter.length()) {
DLOG(L<<Logger::Error<<"skipped, already found shorter best match: "<<j->second.qname<<endl);
continue;
} else if(j->first == shorter.length()) {
DLOG(L<<Logger::Error<<"use shorter best match: "<<j->second.qname<<endl);
*sd = j->second;
break;
} else {
L<<Logger::Error<<"lookup: "<<shorter<<endl;
if((*i)->getAuth(p, sd, shorter)) {
L<<Logger::Error<<"got: "<<sd->qname<<endl;
j->first = sd->qname.length();
if(sd->qname.length() == shorter.length()) {
break;
}
} else {
L<<Logger::Error<<"no match for: "<<shorter<<endl;
}
}
}
if (!chopOff(shorter))
break;
}
}

if( best_match_len == -1 )
return false;

auth_found:
// Insert into cache. Don't cache if the query was a DS
if( d_cache_ttl && ! from_cache && p->qtype != QType::DS ) {
//L<<Logger::Error<<"Saving auth cache for " << sd->qname <<endl;
// Add to cache
if(i == backends.end()) {
if(d_negcache_ttl) {
L<<Logger::Error<<"add neg:"<<shorter<<endl;
d_question.qname=shorter;
addNegCache(d_question);
}
continue;
} else if(d_cache_ttl) {
L<<Logger::Error<<"add pos: "<<sd->qname<<endl;
d_question.qtype = QType::SOA;
d_question.qname = sd->qname;
d_question.zoneId = -1;
Expand All @@ -368,9 +356,20 @@ bool UeberBackend::getAuth(DNSPacket *p, SOAData *sd, const string &target, int
vector<DNSResourceRecord> rrs;
rrs.push_back(rr);
addCache(d_question, rrs);
}
}

found:
if(found == (p->qtype == QType::DS)){
DLOG(L<<Logger::Error<<"found: "<<sd->qname<<endl);
return true;
} else {
DLOG(L<<Logger::Error<<"chasing next: "<<sd->qname<<endl);
found = true;
}

return true;
} while(chopOff(shorter));
return found;
}

/** special trick - if sd.db is set to -1, the cache is ignored */
Expand Down

0 comments on commit 58d6ab6

Please sign in to comment.