#include <cassert>
#include "mind.h"
#include "loghandle.h"
#include "rdata_name.h"
#include "rrcache.h"
#include "rrfactory.h"
#include "watchptr.h"
#include "wwwi/mutexholder.h"
#include "wwwi/rwlocker.h"
using WWWI::MutexHolder;
using WWWI::RWLocker;
RRCache::RRCache() {
m_uEntries = 0;
m_uMaxEntries = 100000;
}
bool RRCache::Add(RRConstPtr ci_rrp) {
RWLocker rk(&m_rw,true);
LogHandle lh(LF_CACHE,LY_DEBUG);
assert(ci_rrp!=NULL);
lh << "cache add " << *ci_rrp << endl; lh();
CacheRRListPtr csp = this->GetNameList(ci_rrp->GetName(),true);
CacheRRPtr crpNew = RRFactory::NewCacheRR(ci_rrp);
return this->Add(csp,crpNew);
}
bool RRCache::Add(CacheRRListPtr io_csp, CacheRRPtr &i_crpr) {
LogHandle lh(LF_CACHE,LY_DEBUG);
CacheRRList::iterator c_crpi;
for(c_crpi=io_csp->begin();c_crpi!=io_csp->end();++c_crpi) {
if ((*c_crpi)->Compare(i_crpr)==true) {
(*c_crpi)->SetTTL(i_crpr->GetTTL());
lh << "cache update " << *i_crpr << endl;
SoftDelete(i_crpr);
i_crpr = NULL;
return false;
} else {
lh << LY_EXTRA_DEBUG << "cache sibling " << **c_crpi << endl; lh();
}
}
lh << "cache insert " << *i_crpr;
io_csp->push_back(i_crpr);
i_crpr = NULL;
m_uEntries++;
lh << " (" << io_csp->size() << "/" << m_uEntries << " entries)" << endl;
if(m_uEntries>m_uMaxEntries) {
lh << LY_INFO << "cache size of " << m_uEntries << " exceeds ceiling of " << m_uMaxEntries << " (requesting expire)" << endl; lh();
this->SetNextExpire(0);
}
return true;
}
void RRCache::Add(ResponseConstPtr ci_rsp) {
assert(ci_rsp!=NULL);
if (ci_rsp==NULL) return;
this->Add(ci_rsp->GetANList());
this->Add(ci_rsp->GetNSList());
this->Add(ci_rsp->GetARList());
}
void RRCache::Add(RRListConstPtr i_rrListPtr) {
if (i_rrListPtr==NULL) return;
RRList::const_iterator it;
for(it=i_rrListPtr->begin();it!=i_rrListPtr->end();++it) {
this->Add(*it);
}
}
bool RRCache::AddRecord(const char *i_strName, RRType i_ty, RRClass i_cl, unsigned i_uTTL, const char *i_strRData) {
LabelListPtr llpName = new LabelList(i_strName);
bool bOut = this->AddRecord(llpName,i_ty,i_cl,i_uTTL,i_strRData);
SoftDelete(llpName);
return bOut;
}
bool RRCache::AddRecord(LabelListConstPtr i_llpName, RRType i_ty, RRClass i_cl, unsigned i_uTTL, const char *i_strRData) {
CacheRRPtr crp;
CacheRRListPtr csp = this->GetNameList(i_llpName,true);
crp = RRFactory::NewCacheRR(i_ty,i_cl,i_uTTL,i_strRData);
return this->Add(csp,crp);
}
void RRCache::AddRootServer(const char *i_strName, const char *i_strIP) {
LabelListPtr llp = new LabelList(i_strName);
CacheRRPtr crp;
crp = RRFactory::NewCacheRR(TY_A,CL_IN,1000000,i_strIP);
this->GetNameList(llp,true)->push_back(crp);
crp = RRFactory::NewCacheRR(TY_NS,CL_IN,1000000,i_strName);
m_cn.cspEntries->push_back(crp);
SoftDelete(llp);
}
void RRCache::Expire() {
RWLocker rk(&m_rw,true);
LogHandle lh(LF_CEXPIRE,LY_INFO);
time_t tmNextExpire;
time_t tmNow;
tmNow = time(NULL);
tmNextExpire = tmNow + 600;
lh << "expire time pass begins with " << m_uEntries << " entries." << endl; lh();
m_uEntries -= m_cn.Expire(tmNow,tmNextExpire);
lh << "expire time pass finishes with " << m_uEntries << " entries." << endl;
while (m_uEntries>m_uMaxEntries) {
tmNow = tmNextExpire + 60;
tmNextExpire = tmNow + 1000000;
lh << "expire space pass begins with " << m_uEntries << " entries." << endl; lh();
m_uEntries -= m_cn.Expire(tmNow,tmNextExpire);
lh << "expire space pass finishes with " << m_uEntries << " entries." << endl;
}
lh << "expire again in " << tmNextExpire-time(NULL) << " seconds." << endl;
this->SetNextExpire(tmNextExpire);
}
unsigned CacheNode::Expire(time_t i_tmNow, time_t &io_tmrNextExpire) {
CacheMap::iterator cmi;
unsigned uReaped = 0;
LogHandle lh(LF_CEXPIRE,LY_DEBUG);
uReaped = cspEntries->Expire(i_tmNow,io_tmrNextExpire);
for(cmi=cmTree.begin(); cmi!=cmTree.end(); ++cmi) {
lh << LY_EXTRA_DEBUG << "expiring at " << (*cmi).first; lh();
uReaped += (*cmi).second->Expire(i_tmNow,io_tmrNextExpire);
}
return uReaped;
}
ResponsePtr RRCache::Get(LabelListConstPtr i_llpName, RRType i_ty, RRClass i_cl) {
RRListPtr rlpAN = NULL;
RRListPtr rlpNS = NULL;
RRListPtr rlpAR = NULL;
WatchPtr<RRListPtr> watchAN(rlpAN);
WatchPtr<RRListPtr> watchAR(rlpAR);
WatchPtr<RRListPtr> watchNS(rlpNS);
assert(i_ty!=TY_QAXFR);
assert(i_ty!=TY_QMAILB);
assert(i_ty!=TY_QMAILA);
{
RWLocker rk(&m_rw,false);
rlpAN = this->GetSimple(i_llpName,i_ty,i_cl);
}
if (rlpAN==NULL) return NULL;
rlpNS = this->GetNS(rlpAN);
rlpAR = this->GetAR(rlpNS);
ResponsePtr rspOut = new Response(false,false,RCODE_NOERROR,rlpAN,rlpNS,rlpAR);
watchAN.Release();
watchNS.Release();
return rspOut;
}
RRListPtr RRCache::GetBestAuthority(LabelListConstPtr ci_llp) const {
RRListPtr rlpOut = new RRList;
this->GetNS(rlpOut,ci_llp);
return rlpOut;
}
CacheRRListPtr RRCache::GetNameList(LabelListConstPtr i_llpName, bool i_bCreate) {
CacheMap::const_iterator cmpi;
LabelList::const_reverse_iterator lbi;
LogHandle lh(LF_CACHE,LY_EXTRA_DEBUG);
CacheNodePtr cnp = &m_cn;
for(lbi=i_llpName->rbegin();lbi!=i_llpName->rend();++lbi) {
lh << (*lbi).GetString();
cmpi = cnp->cmTree.find((*lbi));
if (cmpi==cnp->cmTree.end()) {
if (i_bCreate==false) return NULL;
cnp->cmTree[(*lbi)] = new CacheNode;
cmpi = cnp->cmTree.find((*lbi));
}
cnp = (*cmpi).second;
}
return cnp->cspEntries;
}
CacheRRListConstPtr RRCache::GetNameList(LabelListPtr o_llpMatch, LabelListConstPtr i_llpName, RRType i_ty, RRClass i_cl, bool i_bBest) const {
CacheMap::const_iterator cmi;
LabelList::const_reverse_iterator lbi;
CacheMapConstPtr c_cmp = &m_cn.cmTree;
CacheRRListConstPtr c_csp = m_cn.cspEntries;
CacheRRListConstPtr c_cspOut = m_cn.cspEntries;
LabelListPtr llp = new LabelList;
for(lbi=i_llpName->rbegin();lbi!=i_llpName->rend();++lbi) {
llp->insert(llp->begin(),*lbi);
cmi = c_cmp->find((*lbi));
if (cmi==c_cmp->end()) {
if (i_bBest==false) return NULL;
break;
}
c_csp = (*cmi).second->cspEntries;
c_cmp = &(*cmi).second->cmTree;
if (c_csp->HasTypeClass(i_ty,i_cl)==true) {
if (o_llpMatch!=NULL) (*o_llpMatch) = *llp;
c_cspOut = c_csp;
}
}
SoftDelete(llp);
if ((i_bBest==false)&&(c_csp!=c_cspOut)) return NULL;
return c_cspOut;
}
RRListPtr RRCache::GetNS(RRListPtr i_rlp) const {
RRList::iterator rrpi;
RRListPtr rlpOut = new RRList;
WatchPtr<RRListPtr> wp(rlpOut);
for(rrpi=i_rlp->begin();rrpi!=i_rlp->end();++rrpi) {
this->GetNS(rlpOut,(*rrpi)->GetName());
}
wp.Release();
return rlpOut;
}
void RRCache::GetNS(RRListPtr o_rlp, LabelListConstPtr i_llpName) const {
RWLocker rk(&m_rw,false);
CacheRRListConstPtr c_csp;
CacheRRList::const_iterator c_crpi;
LabelListPtr llpNS = new LabelList;
c_csp = this->GetNameList(llpNS,i_llpName,TY_NS,CL_IN,true);
if (c_csp!=NULL) {
for(c_crpi=c_csp->begin();c_crpi!=c_csp->end();++c_crpi) {
if ((*c_crpi)->IsTypeClass(TY_NS,CL_IN)==false) continue;
if (o_rlp->HasResourceMatch(*c_crpi)==true) continue;
LabelListPtr llp = new LabelList(*llpNS);
o_rlp->push_back(RRFactory::NewRR(llp,*c_crpi));
}
}
SoftDelete(llpNS);
}
RRListPtr RRCache::GetAR(RRListPtr i_rlpNS) {
RRList::iterator rliNS;
RRList::iterator rliTemp;
RRListPtr rlpOut = new RRList;
RRListPtr rlpTemp;
for(rliNS=i_rlpNS->begin();rliNS!=i_rlpNS->end();++rliNS) {
rlpTemp = this->GetSimple( (*rliNS)->GetRData()->As<RDataName>()->GetRDName(), TY_A, CL_IN);
if (rlpTemp!=NULL) {
for(rliTemp=rlpTemp->begin(); rliTemp!=rlpTemp->end(); ++rliTemp) {
rlpOut->push_back( *rliTemp );
}
rlpTemp->erase(rlpTemp->begin(),rlpTemp->end());
SoftDelete(rlpTemp);
}
}
return rlpOut;
}
RRListPtr RRCache::GetSimple(LabelListConstPtr ci_llpName, RRType i_ty, RRClass i_cl) {
CacheRRListPtr csp;
CacheRRList::const_iterator cspi;
unsigned uNow = time(NULL);
RRListPtr rlpOut = NULL;
LogHandle lh(LF_CACHE,LY_DEBUG);
csp = this->GetNameList(ci_llpName,false);
if (csp==NULL) return NULL;
lh << "cache check " << ci_llpName << " " << GetClassName(i_cl) << " " << GetTypeName(i_ty) << endl;
assert(i_ty!=TY_QAXFR);
assert(i_ty!=TY_QMAILB);
assert(i_ty!=TY_QMAILA);
rlpOut = new RRList;
for(cspi=csp->begin();cspi!=csp->end();++cspi) {
lh << LY_EXTRA_DEBUG << "cache try " << *(*cspi) << endl; lh();
if ((*cspi)->IsExpired(uNow)==true) {
lh << "cache stale " << **cspi << endl;
continue;
}
if ( (*cspi)->IsTypeClass(i_ty,i_cl) == false) {
if ( (*cspi)->IsTypeClass(TY_CNAME,i_cl) == false) continue;
}
LabelListPtr llp = new LabelList(*ci_llpName);
lh << "cache hit " << llp << " " << *(*cspi) << endl;
rlpOut->push_back(RRFactory::NewRR(llp,(*cspi)));
}
if (rlpOut->size()==0) {
lh << "cache miss " << ci_llpName << " " << GetClassName(i_cl) << " " << GetTypeName(i_ty) << endl;
SoftDelete(rlpOut);
return NULL;
}
return rlpOut;
}
void RRCache::Walk(ostream &io_smr) const {
io_smr << "." << endl;
m_cn.Walk(io_smr,1);
}
void CacheNode::Walk(ostream &io_smr, int i_iDepth) const {
CacheMap::const_iterator cmi;
int i;
cspEntries->Walk(io_smr,i_iDepth);
for(cmi=cmTree.begin();cmi!=cmTree.end();++cmi) {
for(i=0;i<i_iDepth;++i) io_smr << " ";
io_smr << (*cmi).first << endl;
(*cmi).second->Walk(io_smr,i_iDepth+1);
}
}
RRCache::~RRCache() {
}
CacheNode::~CacheNode() {
CacheMap::iterator cmi;
SoftDelete(cspEntries);
for(cmi=cmTree.begin();cmi!=cmTree.end();++cmi) {
delete (*cmi).second;
}
}