]> granicus.if.org Git - pdns/commitdiff
it is a ip
authorbert hubert <bert.hubert@netherlabs.nl>
Wed, 12 Mar 2014 16:05:57 +0000 (17:05 +0100)
committerbert hubert <bert.hubert@netherlabs.nl>
Wed, 12 Mar 2014 16:05:57 +0000 (17:05 +0100)
modules/bindbackend/bindbackend2.cc
modules/bindbackend/bindbackend2.hh

index 460ef6898fac2243c0b214c4cb7ccc1106e05c16..8624d7160eaa8861c4bcb198cf6967feb65f5016 100644 (file)
@@ -58,8 +58,7 @@
     on start of query, we find the best zone to answer from
 */
 
-// this map contains BB2DomainInfo structs, each of which contains a *pointer* to domain data
-shared_ptr<Bind2Backend::State> Bind2Backend::s_state;
+Bind2Backend::state_t Bind2Backend::s_state;
 
 /* the model is that all our state hides in s_state. This State instance consists of the id_zone_map, which contains all our zone information, indexed by id.
    Then there is the name_id_map, which allows us to map a query to a zone id.
@@ -82,9 +81,7 @@ shared_ptr<Bind2Backend::State> Bind2Backend::s_state;
 int Bind2Backend::s_first=1;
 bool Bind2Backend::s_ignore_broken_records=false;
 
-pthread_mutex_t Bind2Backend::s_startup_lock=PTHREAD_MUTEX_INITIALIZER;
-pthread_mutex_t Bind2Backend::s_state_lock=PTHREAD_MUTEX_INITIALIZER;
-pthread_mutex_t Bind2Backend::s_state_swap_lock=PTHREAD_MUTEX_INITIALIZER;
+pthread_rwlock_t Bind2Backend::s_state_lock;
 pthread_mutex_t Bind2Backend::s_supermaster_config_lock=PTHREAD_MUTEX_INITIALIZER;
 string Bind2Backend::s_binddirectory;  
 /* when a query comes in, we find the most appropriate zone and answer from that */
@@ -140,24 +137,19 @@ void BB2DomainInfo::setCtime()
 
 void Bind2Backend::setNotified(uint32_t id, uint32_t serial)
 {
-  Lock l(&s_state_lock);
-  s_state->id_zone_map[id].d_lastnotified=serial;
+  BB2DomainInfo bbd;
+  safeGetBBDomainInfo(id, &bbd);
+  bbd.d_lastnotified = serial;
+  safePutBBDomainInfo(id, bbd);
 }
 
 void Bind2Backend::setFresh(uint32_t domain_id)
 {
-  Lock l(&s_state_lock);
-  s_state->id_zone_map[domain_id].d_lastcheck=time(0);
-}
-
-shared_ptr<Bind2Backend::State> Bind2Backend::getState()
-{
-  shared_ptr<State> ret;
-  {
-    Lock l(&s_state_swap_lock);
-    ret = s_state; // is only read from
+  BB2DomainInfo bbd;
+  if(safeGetBBDomainInfo(domain_id, &bbd)) {
+    bbd.d_lastcheck=time(0);
+    safePutBBDomainInfo(domain_id, bbd);
   }
-  return ret;
 }
 
 bool Bind2Backend::startTransaction(const string &qname, int id)
@@ -170,23 +162,34 @@ bool Bind2Backend::startTransaction(const string &qname, int id)
   if(id == 0) {
     throw DBException("domain_id 0 is invalid for this backend.");
   }
-  shared_ptr<State> state = getState(); 
 
-  const BB2DomainInfo &bbd=state->id_zone_map[d_transaction_id=id];
-
-  d_transaction_tmpname=bbd.d_filename+"."+itoa(random());
-  d_of=new ofstream(d_transaction_tmpname.c_str());
-  if(!*d_of) {
-    throw DBException("Unable to open temporary zonefile '"+d_transaction_tmpname+"': "+stringerror());
-    unlink(d_transaction_tmpname.c_str());
-    delete d_of;
-    d_of=0;
+  d_transaction_id=id;
+  BB2DomainInfo bbd;
+  if(safeGetBBDomainInfo(id, &bbd)) {
+    d_transaction_tmpname=bbd.d_filename+"."+itoa(random());
+    d_of=new ofstream(d_transaction_tmpname.c_str());
+    if(!*d_of) {
+      throw DBException("Unable to open temporary zonefile '"+d_transaction_tmpname+"': "+stringerror());
+      unlink(d_transaction_tmpname.c_str());
+      delete d_of;
+      d_of=0;
+    }
+    
+    *d_of<<"; Written by PowerDNS, don't edit!"<<endl;
+    *d_of<<"; Zone '"+bbd.d_name+"' retrieved from master "<<endl<<"; at "<<nowTime()<<endl; // insert master info here again
+    
+    return true;
   }
-  
-  *d_of<<"; Written by PowerDNS, don't edit!"<<endl;
-  *d_of<<"; Zone '"+bbd.d_name+"' retrieved from master "<<endl<<"; at "<<nowTime()<<endl; // insert master info here again
+  return false;
+}
 
-  return true;
+bool Bind2Backend::safeGetBBDomainInfo(int id, BB2DomainInfo* bbd)
+{
+  ReadLock rl(&s_state_lock);
+  state_t::const_iterator iter = s_state.find(id);
+  if(iter == s_state.end())
+    return false;
+  *bbd=*iter;
 }
 
 bool Bind2Backend::commitTransaction()
@@ -195,13 +198,13 @@ bool Bind2Backend::commitTransaction()
     return true;
   delete d_of;
   d_of=0;
-  shared_ptr<State> state = getState(); 
 
-  // this might fail if s_state was cycled during the AXFR
-  if(rename(d_transaction_tmpname.c_str(), state->id_zone_map[d_transaction_id].d_filename.c_str())<0)
-    throw DBException("Unable to commit (rename to: '" + state->id_zone_map[d_transaction_id].d_filename+"') AXFRed zone: "+stringerror());
-
-  queueReload(&state->id_zone_map[d_transaction_id]);
+  BB2DomainInfo bbd;
+  if(safeGetBBDomainInfo(d_transaction_id, &bbd)) {
+    if(rename(d_transaction_tmpname.c_str(), bbd.d_filename.c_str())<0)
+    throw DBException("Unable to commit (rename to: '" + bbd.d_filename+"') AXFRed zone: "+stringerror());
+    queueReload(&bbd);
+  }
 
   d_transaction_id=0;
 
@@ -227,8 +230,10 @@ bool Bind2Backend::feedRecord(const DNSResourceRecord &r, string *ordername)
 {
   string qname=r.qname;
 
-  const shared_ptr<State> state = getState();
-  string domain = state->id_zone_map[d_transaction_id].d_name;
+  BB2DomainInfo bbd;
+  safeGetBBDomainInfo(d_transaction_id, &bbd);
+
+  string domain = bbd.d_name;
 
   if(!stripDomainSuffix(&qname,domain)) 
     throw DBException("out-of-zone data '"+qname+"' during AXFR of zone '"+domain+"'");
@@ -263,48 +268,52 @@ bool Bind2Backend::feedRecord(const DNSResourceRecord &r, string *ordername)
 void Bind2Backend::getUpdatedMasters(vector<DomainInfo> *changedDomains)
 {
   SOAData soadata;
-  shared_ptr<State> state = getState(); 
+  ReadLock rl(&s_state_lock);
 
-  for(id_zone_map_t::const_iterator i = state->id_zone_map.begin(); i != state->id_zone_map.end() ; ++i) {
-    if(!i->second.d_masters.empty() && this->alsoNotify.empty() && i->second.d_also_notify.empty())
+  for(state_t::const_iterator i = s_state.begin(); i != s_state.end() ; ++i) {
+    if(!i->d_masters.empty() && this->alsoNotify.empty() && i->d_also_notify.empty())
       continue;
     soadata.serial=0;
     try {
-      this->getSOA(i->second.d_name, soadata); // we might not *have* a SOA yet, but this might trigger a load of it
+      this->getSOA(i->d_name, soadata); // we might not *have* a SOA yet, but this might trigger a load of it
     }
     catch(...){}
     DomainInfo di;
-    di.id=i->first;
+    di.id=i->d_id;
     di.serial=soadata.serial;
-    di.zone=i->second.d_name;
-    di.last_check=i->second.d_lastcheck;
+    di.zone=i->d_name;
+    di.last_check=i->d_lastcheck;
     di.backend=this;
     di.kind=DomainInfo::Master;
-    if(!i->second.d_lastnotified)  {          // don't do notification storm on startup 
-      Lock l(&s_state_lock);
-      s_state->id_zone_map[i->first].d_lastnotified=soadata.serial;
+    if(!i->d_lastnotified)  {          // don't do notification storm on startup 
+      // what if i->first is new??
+      BB2DomainInfo bbd;
+      if(safeGetBBDomainInfo(i->d_id, &bbd)) {
+       bbd.d_lastnotified=soadata.serial; 
+       safePutBBDomainInfo(i->d_id, bbd);
+      }
     }
     else
-      if(soadata.serial!=i->second.d_lastnotified)
+      if(soadata.serial != i->d_lastnotified)
         changedDomains->push_back(di);
   }
 }
 
-void Bind2Backend::getAllDomains(vector<DomainInfo> *domains, bool include_disabled) {
+void Bind2Backend::getAllDomains(vector<DomainInfo> *domains, bool include_disabled) 
+{
+  ReadLock rl(&s_state_lock);
   SOAData soadata;
 
-  shared_ptr<State> state = getState(); 
-
-  for(id_zone_map_t::const_iterator i = state->id_zone_map.begin(); i != state->id_zone_map.end() ; ++i) {
+  for(state_t::const_iterator i = s_state.begin(); i != s_state.end() ; ++i) {
     soadata.db=(DNSBackend *)-1; // makes getSOA() skip the cache. 
-    this->getSOA(i->second.d_name, soadata);
+    this->getSOA(i->d_name, soadata);
     DomainInfo di;
-    di.id=i->first;
+    di.id=i->d_id;
     di.serial=soadata.serial;
-    di.zone=i->second.d_name;
-    di.last_check=i->second.d_lastcheck;
+    di.zone=i->d_name;
+    di.last_check=i->d_lastcheck;
     di.backend=this;
-    di.kind=i->second.d_masters.empty() ? DomainInfo::Master : DomainInfo::Slave; //TODO: what about Native?
+    di.kind=i->d_masters.empty() ? DomainInfo::Master : DomainInfo::Slave; //TODO: what about Native?
 
     domains->push_back(di);
   }
@@ -313,15 +322,15 @@ void Bind2Backend::getAllDomains(vector<DomainInfo> *domains, bool include_disab
 
 void Bind2Backend::getUnfreshSlaveInfos(vector<DomainInfo> *unfreshDomains)
 {
-  shared_ptr<State> state = getState();
-  for(id_zone_map_t::const_iterator i = state->id_zone_map.begin(); i != state->id_zone_map.end() ; ++i) {
-    if(i->second.d_masters.empty())
+  ReadLock rl(&s_state_lock);
+  for(state_t::const_iterator i = s_state.begin(); i != s_state.end() ; ++i) {
+    if(i->d_masters.empty())
       continue;
     DomainInfo sd;
-    sd.id=i->first;
-    sd.zone=i->second.d_name;
-    sd.masters=i->second.d_masters;
-    sd.last_check=i->second.d_lastcheck;
+    sd.id=i->d_id;
+    sd.zone=i->d_name;
+    sd.masters=i->d_masters;
+    sd.last_check=i->d_lastcheck;
     sd.backend=this;
     sd.kind=DomainInfo::Slave;
     SOAData soadata;
@@ -329,7 +338,7 @@ void Bind2Backend::getUnfreshSlaveInfos(vector<DomainInfo> *unfreshDomains)
     soadata.serial=0;
     soadata.db=(DNSBackend *)-1; // not sure if this is useful, inhibits any caches that might be around
     try {
-      getSOA(i->second.d_name,soadata); // we might not *have* a SOA yet
+      getSOA(i->d_name,soadata); // we might not *have* a SOA yet
     }
     catch(...){}
     sd.serial=soadata.serial;
@@ -340,21 +349,21 @@ void Bind2Backend::getUnfreshSlaveInfos(vector<DomainInfo> *unfreshDomains)
 
 bool Bind2Backend::getDomainInfo(const string &domain, DomainInfo &di)
 {
-  shared_ptr<State> state = getState();
-  for(id_zone_map_t::const_iterator i = state->id_zone_map.begin(); i != state->id_zone_map.end() ; ++i) { // why is this a linear scan??
-    if(pdns_iequals(i->second.d_name,domain)) {
-      di.id=i->first;
+  ReadLock rl(&s_state_lock);
+  for(state_t::const_iterator i = s_state.begin(); i != s_state.end() ; ++i) {
+    if(pdns_iequals(i->d_name,domain)) {
+      di.id=i->d_id;
       di.zone=domain;
-      di.masters=i->second.d_masters;
-      di.last_check=i->second.d_lastcheck;
+      di.masters=i->d_masters;
+      di.last_check=i->d_lastcheck;
       di.backend=this;
-      di.kind=i->second.d_masters.empty() ? DomainInfo::Master : DomainInfo::Slave;
+      di.kind=i->d_masters.empty() ? DomainInfo::Master : DomainInfo::Slave;
       di.serial=0;
       try {
         SOAData sd;
         sd.serial=0;
         
-        getSOA(i->second.d_name,sd); // we might not *have* a SOA yet
+        getSOA(i->d_name,sd); // we might not *have* a SOA yet
         di.serial=sd.serial;
       }
       catch(...){}
@@ -367,14 +376,14 @@ bool Bind2Backend::getDomainInfo(const string &domain, DomainInfo &di)
 
 void Bind2Backend::alsoNotifies(const string &domain, set<string> *ips)
 {
-  shared_ptr<State> state = getState();
+  ReadLock rl(&s_state_lock);
   // combine global list with local list
   for(set<string>::iterator i = this->alsoNotify.begin(); i != this->alsoNotify.end(); i++) {
     (*ips).insert(*i);
   }
-  for(id_zone_map_t::const_iterator i = state->id_zone_map.begin(); i != state->id_zone_map.end() ; ++i) {
-    if(i->second.d_name==domain) {
-      for(set<string>::iterator it = i->second.d_also_notify.begin(); it != i->second.d_also_notify.end(); it++) {
+  for(state_t::const_iterator i = s_state.begin(); i != s_state.end() ; ++i) {
+    if(pdns_iequals(i->d_name,domain)) {
+      for(set<string>::iterator it = i->d_also_notify.begin(); it != i->d_also_notify.end(); it++) {
         (*ips).insert(*it);
       }
       return;
@@ -398,12 +407,14 @@ static string canonic(string ret)
   return ret;
 }
 
-void Bind2Backend::parseZoneFile(shared_ptr<State> staging, BB2DomainInfo *bbd)
+void Bind2Backend::parseZoneFile(BB2DomainInfo *bbd)
 {
   NSEC3PARAMRecordContent ns3pr;
   bool nsec3zone=getNSEC3PARAM(bbd->d_name, &ns3pr);
         
   ZoneParserTNG zpt(bbd->d_filename, bbd->d_name, s_binddirectory);
+  bbd->d_records.getWRITABLE().reset();
+
   DNSResourceRecord rr;
   string hashed;
   while(zpt.get(rr)) {  // FIXME this code is duplicate
@@ -416,11 +427,11 @@ void Bind2Backend::parseZoneFile(shared_ptr<State> staging, BB2DomainInfo *bbd)
       else
         hashed="";
     }
-    insert(staging, bbd->d_id, rr.qname, rr.qtype, rr.content, rr.ttl, rr.priority, hashed);
+    insert(*bbd, rr.qname, rr.qtype, rr.content, rr.ttl, rr.priority, hashed);
   }
 
-  fixupAuth(staging->id_zone_map[bbd->d_id].d_records);
-  doEmptyNonTerminals(staging, bbd->d_id, nsec3zone, ns3pr);
+  fixupAuth(bbd->d_records.getWRITABLE());
+  doEmptyNonTerminals(*bbd, nsec3zone, ns3pr);
 
   bbd->setCtime();
   bbd->d_loaded=true; 
@@ -428,15 +439,11 @@ void Bind2Backend::parseZoneFile(shared_ptr<State> staging, BB2DomainInfo *bbd)
 }
 
 /** THIS IS AN INTERNAL FUNCTION! It does moadnsparser prio impedance matching
-    This function adds a record to a domain with a certain id. 
     Much of the complication is due to the efforts to benefit from std::string reference counting copy on write semantics */
-void Bind2Backend::insert(shared_ptr<State> stage, int id, const string &qnameu, const QType &qtype, const string &content, int ttl, int prio, const std::string& hashed, bool *auth)
+void Bind2Backend::insert(BB2DomainInfo& bb2, const string &qnameu, const QType &qtype, const string &content, int ttl, int prio, const std::string& hashed, bool *auth)
 {
-  BB2DomainInfo bb2 = stage->id_zone_map[id];
   Bind2DNSRecord bdr;
-
-  recordstorage_t& records=*bb2.d_records; 
-
+  shared_ptr<recordstorage_t> records = bb2.d_records.getWRITABLE();
   bdr.qname=canonic(qnameu);
   //cerr << "qname = " << bdr.qname << ", d_name = " << bb2.d_name << endl;
   if(bb2.d_name.empty())
@@ -455,9 +462,8 @@ void Bind2Backend::insert(shared_ptr<State> stage, int id, const string &qnameu,
 
   bdr.qname.swap(bdr.qname);
 
-
-  if(!records.empty() && bdr.qname==boost::prior(records.end())->qname)
-    bdr.qname=boost::prior(records.end())->qname;
+  if(!records->empty() && bdr.qname==boost::prior(records->end())->qname)
+    bdr.qname=boost::prior(records->end())->qname;
 
   //  cerr<<"Before reverse: '"<<bdr.qname<<"', ";
   bdr.qname=labelReverse(bdr.qname);
@@ -486,21 +492,21 @@ void Bind2Backend::insert(shared_ptr<State> stage, int id, const string &qnameu,
   bdr.ttl=ttl;
   bdr.priority=prio;
   
-  records.insert(bdr);
+  records->insert(bdr);
 }
 
 
 string Bind2Backend::DLReloadNowHandler(const vector<string>&parts, Utility::pid_t ppid)
 {
-  shared_ptr<State> state = getState();
   ostringstream ret;
 
   for(vector<string>::const_iterator i=parts.begin()+1;i<parts.end();++i) {
-    if(state->name_id_map.count(*i)) {
-      BB2DomainInfo& bbd=state->id_zone_map[state->name_id_map[*i]];
+    BB2DomainInfo bbd;
+    if(safeGetBBDomainInfo(*i, &bbd)) {
       Bind2Backend bb2;
       bb2.queueReload(&bbd);
       ret<< *i << ": "<< (bbd.d_loaded ? "": "[rejected]") <<"\t"<<bbd.d_status<<"\n";      
+      // XXX supplant new bbd HOW?
     }
     else
       ret<< *i << " no such domain\n";
@@ -514,21 +520,23 @@ string Bind2Backend::DLReloadNowHandler(const vector<string>&parts, Utility::pid
 string Bind2Backend::DLDomStatusHandler(const vector<string>&parts, Utility::pid_t ppid)
 {
   ostringstream ret;
-  shared_ptr<State> state = getState();
       
   if(parts.size() > 1) {
     for(vector<string>::const_iterator i=parts.begin()+1;i<parts.end();++i) {
-      if(state->name_id_map.count(*i)) {
-        BB2DomainInfo& bbd=state->id_zone_map[state->name_id_map[*i]];  // XXX s_name_id_map needs trick as well
+      BB2DomainInfo bbd;
+      if(safeGetBBDomainInfo(*i, &bbd)) {      
         ret<< *i << ": "<< (bbd.d_loaded ? "": "[rejected]") <<"\t"<<bbd.d_status<<"\n";      
     }
       else
         ret<< *i << " no such domain\n";
     }    
   }
-  else
-    for(id_zone_map_t::iterator i=state->id_zone_map.begin(); i!=state->id_zone_map.end(); ++i) 
-      ret<< i->second.d_name << ": "<< (i->second.d_loaded ? "": "[rejected]") <<"\t"<<i->second.d_status<<"\n";      
+  else {
+    ReadLock rl(&s_state_lock);
+    for(state_t::const_iterator i = s_state.begin(); i != s_state.end() ; ++i) {
+      ret<< i->d_name << ": "<< (i->d_loaded ? "": "[rejected]") <<"\t"<<i->d_status<<"\n";      
+    }
+  }
 
   if(ret.str().empty())
     ret<<"no domains passed";
@@ -539,13 +547,13 @@ string Bind2Backend::DLDomStatusHandler(const vector<string>&parts, Utility::pid
 
 string Bind2Backend::DLListRejectsHandler(const vector<string>&parts, Utility::pid_t ppid)
 {
-  shared_ptr<State> state = getState();
-
+  ReadLock rl(&s_state_lock);
   ostringstream ret;
-  for(id_zone_map_t::iterator j = state->id_zone_map.begin(); j != state->id_zone_map.end(); ++j) 
-    if(!j->second.d_loaded)
-      ret<<j->second.d_name<<"\t"<<j->second.d_status<<endl;
+  for(state_t::const_iterator i = s_state.begin(); i != s_state.end() ; ++i) {
+    if(!i->d_loaded)
+      ret<<i->d_name<<"\t"<<i->d_status<<endl;
         
+  }
   return ret.str();
 }
 
@@ -556,23 +564,20 @@ string Bind2Backend::DLAddDomainHandler(const vector<string>&parts, Utility::pid
 
   string domainname = canonic(parts[1]);
   const string &filename = parts[2];
-
-  if(getState()->name_id_map.count(domainname))
+  BB2DomainInfo bbd;
+  if(safeGetBBDomainInfo(domainname, &bbd))
     return "Already loaded";
 
-  // Interference with loadConfig() and createSlaveDomain(), use locking
-  Lock l(&s_state_lock);
-
   Bind2Backend bb2;
-  BB2DomainInfo& bbd = bb2.createDomainEntry(domainname, filename);
+  bb2.createDomainEntry(domainname, filename);
 
   bbd.d_filename=filename;
   bbd.d_checknow=true;
   bbd.d_loaded=true;
   bbd.d_lastcheck=0;
   bbd.d_status="parsing into memory";
-          
-  s_state->name_id_map[bbd.d_name]=bbd.d_id;
+
+  /// XXX ALL WRONG SUPPLANT
 
   L<<Logger::Warning<<"Zone "<<domainname<< " loaded"<<endl;
 
@@ -593,7 +598,6 @@ Bind2Backend::Bind2Backend(const string &suffix, bool loadZones)
     return;
   }
   
-  s_state = shared_ptr<State>(new State);
   if(loadZones) {
     loadConfig();
     s_first=0;
@@ -617,9 +621,10 @@ void Bind2Backend::rediscover(string *status)
 
 void Bind2Backend::reload()
 {
-  Lock l(&s_state_lock);
-  for(id_zone_map_t::iterator i = s_state->id_zone_map.begin(); i != s_state->id_zone_map.end(); ++i) 
-    i->second.d_checknow=true;
+  WriteLock rwl(&s_state_lock);
+  for(state_t::const_iterator i = s_state.begin(); i != s_state.end() ; ++i) {
+    i->d_checknow=true;
+  }
 }
 
 void Bind2Backend::fixupAuth(shared_ptr<recordstorage_t> records)
@@ -651,10 +656,9 @@ void Bind2Backend::fixupAuth(shared_ptr<recordstorage_t> records)
   }
 }
 
-void Bind2Backend::doEmptyNonTerminals(shared_ptr<State> stage, int id, bool nsec3zone, NSEC3PARAMRecordContent ns3pr)
+void Bind2Backend::doEmptyNonTerminals(BB2DomainInfo& bbd, bool nsec3zone, NSEC3PARAMRecordContent ns3pr)
 {
-  BB2DomainInfo bb2 = stage->id_zone_map[id];
-
+  shared_ptr<recordstorage_t> records = bbd.d_records.getWRITABLE();
   bool auth, doent=true;
   set<string> qnames;
   map<string, bool> nonterm;
@@ -662,10 +666,10 @@ void Bind2Backend::doEmptyNonTerminals(shared_ptr<State> stage, int id, bool nse
 
   uint32_t maxent = ::arg().asNum("max-ent-entries");
 
-  BOOST_FOREACH(const Bind2DNSRecord& bdr, *bb2.d_records)
+  BOOST_FOREACH(const Bind2DNSRecord& bdr, *records)
     qnames.insert(labelReverse(bdr.qname));
 
-  BOOST_FOREACH(const Bind2DNSRecord& bdr, *bb2.d_records) {
+  BOOST_FOREACH(const Bind2DNSRecord& bdr, *records) {
     shorter=labelReverse(bdr.qname);
 
     if (!bdr.auth && bdr.qtype == QType::NS)
@@ -679,7 +683,7 @@ void Bind2Backend::doEmptyNonTerminals(shared_ptr<State> stage, int id, bool nse
       {
         if(!(maxent))
         {
-          L<<Logger::Error<<"Zone '"<<bb2.d_name<<"' has too many empty non terminals."<<endl;
+          L<<Logger::Error<<"Zone '"<<bbd.d_name<<"' has too many empty non terminals."<<endl;
           doent=false;
           break;
         }
@@ -703,22 +707,17 @@ void Bind2Backend::doEmptyNonTerminals(shared_ptr<State> stage, int id, bool nse
   pair<string, bool> nt;
   BOOST_FOREACH(nt, nonterm)
   {
-    rr.qname=nt.first+"."+bb2.d_name+".";
+    rr.qname=nt.first+"."+bbd.d_name+".";
     if(nsec3zone)
       hashed=toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, rr.qname));
-    insert(stage, id, rr.qname, rr.qtype, rr.content, rr.ttl, rr.priority, hashed, &nt.second);
+    insert(bbd, rr.qname, rr.qtype, rr.content, rr.ttl, rr.priority, hashed, &nt.second);
   }
 }
 
 void Bind2Backend::loadConfig(string* status)
 {
-  // Interference with createDomainEntry()
-  Lock l(&s_state_lock);
-
   static int domain_id=1;
 
-  shared_ptr<State> staging = shared_ptr<State>(new State);
-
   if(!getArg("config").empty()) {
     BindParser BP;
     try {
@@ -761,44 +760,31 @@ void Bind2Backend::loadConfig(string* status)
           continue;
         }
 
-        BB2DomainInfo* bbd=0;
-
-        if(!s_state->name_id_map.count(i->name)) { // is it fully new?
-          bbd=&staging->id_zone_map[domain_id];
-          bbd->d_id=domain_id++;
-        
-          // this isn't necessary, we do this on the actual load
-          //     bbd->d_records=shared_ptr<recordstorage_t > (new recordstorage_t);
+        BB2DomainInfo bbd;
 
-          bbd->setCheckInterval(getArgAsNum("check-interval"));
-          bbd->d_lastnotified=0;
-          bbd->d_loaded=false;
-        }
-        else {  // no, we knew about it already
-          staging->id_zone_map[s_state->name_id_map[i->name]] = s_state->id_zone_map[s_state->name_id_map[i->name]]; // these should all be read-only on s_state
-          bbd = &staging->id_zone_map[s_state->name_id_map[i->name]];
+        if(!safeGetBBDomainInfo(i->name, &bbd)) { 
+          bbd.d_id=domain_id++;
+          bbd.setCheckInterval(getArgAsNum("check-interval"));
+          bbd.d_lastnotified=0;
+          bbd.d_loaded=false;
         }
         
-        staging->name_id_map[i->name]=bbd->d_id; // fill out name -> id map
-
         // overwrite what we knew about the domain
-        bbd->d_name=toLower(canonic(i->name));
+        bbd.d_name=toLower(canonic(i->name));
 
-        bool filenameChanged = (bbd->d_filename!=i->filename);
-        bbd->d_filename=i->filename;
-        bbd->d_masters=i->masters;
-        bbd->d_also_notify=i->alsoNotify;
+        bool filenameChanged = (bbd.d_filename!=i->filename);
+        bbd.d_filename=i->filename;
+        bbd.d_masters=i->masters;
+        bbd.d_also_notify=i->alsoNotify;
         
-        if(filenameChanged || !bbd->d_loaded || !bbd->current()) {
+        if(filenameChanged || !bbd.d_loaded || !bbd.current()) {
           L<<Logger::Info<<d_logprefix<<" parsing '"<<i->name<<"' from file '"<<i->filename<<"'"<<endl;
 
           try {
             // we need to allocate a new vector so we don't kill the original, which is still in use!
-            bbd->d_records=shared_ptr<recordstorage_t> (new recordstorage_t()); 
+            bbd.d_records=shared_ptr<recordstorage_t> (new recordstorage_t()); 
 
-            parseZoneFile(staging, bbd);
-
-            //  s_stage->id_zone_map[bbd->d_id].d_records->swap(*s_staging_zone_map[bbd->d_id].d_records);
+            parseZoneFile(&bbd);
           }
           catch(PDNSException &ae) {
             ostringstream msg;
@@ -806,7 +792,8 @@ void Bind2Backend::loadConfig(string* status)
 
             if(status)
               *status+=msg.str();
-            staging->id_zone_map[bbd->d_id].d_status=msg.str();
+           bbd.d_status=msg.str();
+
             L<<Logger::Warning<<d_logprefix<<msg.str()<<endl;
             rejected++;
           }
@@ -816,61 +803,36 @@ void Bind2Backend::loadConfig(string* status)
 
             if(status)
               *status+=msg.str();
-            staging->id_zone_map[bbd->d_id].d_status=msg.str();
+            bbd.d_status=msg.str();
             L<<Logger::Warning<<d_logprefix<<msg.str()<<endl;
             rejected++;
           }
         }
-        /*
-        vector<vector<BBResourceRecord> *>&tmp=d_zone_id_map[bbd.d_id];  // shrink trick
-        vector<vector<BBResourceRecord> *>(tmp).swap(tmp);
-        */
       }
 
     // figure out which domains were new and which vanished
     int remdomains=0;
     set<string> oldnames, newnames;
-    for(id_zone_map_t::const_iterator j=s_state->id_zone_map.begin();j != s_state->id_zone_map.end();++j) {
+#if 0
+    for(state_t::const_iterator j=state.get()->id_zone_map.begin();j != s_state.get()->id_zone_map.end();++j) {
       oldnames.insert(j->second.d_name);
     }
     for(id_zone_map_t::const_iterator j=staging->id_zone_map.begin(); j!= staging->id_zone_map.end(); ++j) {
       newnames.insert(j->second.d_name);
     }
-
+#endif
     vector<string> diff;
+
     set_difference(oldnames.begin(), oldnames.end(), newnames.begin(), newnames.end(), back_inserter(diff));
     remdomains=diff.size();
 
-#if 0        
-    // remove domains from the *name* map, delete their pointer
-    for(vector<string>::const_iterator k=diff.begin();k!=diff.end(); ++k) {
-      L<<Logger::Error<<"Removing domain: "<<*k<<endl;
-      s_state->name_id_map.erase(*k);
-    }
-
-    // now remove from the s_state->id_zone_map
-    for(id_zone_map_t::iterator j=s_state->id_zone_map.begin();j!=s_state->id_zone_map.end();++j) { // O(N*M)
-      for(vector<string>::const_iterator k=diff.begin();k!=diff.end();++k)
-        if(j->second.d_name==*k) {
-          L<<Logger::Error<<"Removing records from zone '"<<j->second.d_name<<"' from memory"<<endl;
-
-          j->second.d_loaded=false;
-          nukeZoneRecords(&j->second);
-
-          break;
-        }
-    }
-#endif
-
     // count number of entirely new domains
     vector<string> diff2;
     set_difference(newnames.begin(), newnames.end(), oldnames.begin(), oldnames.end(), back_inserter(diff2));
     newdomains=diff2.size();
     
-    Lock l(&s_state_swap_lock);
-    s_state.swap(staging); 
+    // NOW DO MAGIC STUFF SO THINGS WORK
 
-    // report
     ostringstream msg;
     msg<<" Done parsing domains, "<<rejected<<" rejected, "<<newdomains<<" new, "<<remdomains<<" removed"; 
     if(status)
@@ -880,38 +842,16 @@ void Bind2Backend::loadConfig(string* status)
   }
 }
 
-/** nuke all records from memory, keep bbd intact though. */
-void Bind2Backend::nukeZoneRecords(BB2DomainInfo *bbd)
-{
-  bbd->d_loaded=0; // block further access
-  bbd->d_records = shared_ptr<recordstorage_t > (new recordstorage_t);
-}
-
 
 void Bind2Backend::queueReload(BB2DomainInfo *bbd)
 {
-  Lock l(&s_state_lock);
-
-  shared_ptr<State> staging(new State);
-
-  // we reload *now* for the time being
-
   try {
-    // nukeZoneRecords(bbd); // ? do we need this?
-    staging->id_zone_map[bbd->d_id]=s_state->id_zone_map[bbd->d_id];
+    BB2DomainInfo bbold;
+    safeGetBBDomainInfo(bbd->d_id, &bbold);
     shared_ptr<recordstorage_t > newrecords(new recordstorage_t);
-    staging->id_zone_map[bbd->d_id].d_records=newrecords;
-
-    parseZoneFile(staging, &staging->id_zone_map[bbd->d_id]);
-    staging->id_zone_map[bbd->d_id].setCtime();
-
-    s_state->id_zone_map[bbd->d_id]=staging->id_zone_map[bbd->d_id]; // move over
-
-    // bbd can still be in use in another thread so update it as well
-    // but dont update ctime and status on that copy
-    bbd->d_loaded=true;
-    bbd->d_checknow=false;
+    parseZoneFile(&bbold);
 
+    safePutBBDomainInfo(bbd->d_id, bbold);
     L<<Logger::Warning<<"Zone '"<<bbd->d_name<<"' ("<<bbd->d_filename<<") reloaded"<<endl;
   }
   catch(PDNSException &ae) {
@@ -930,13 +870,14 @@ bool Bind2Backend::findBeforeAndAfterUnhashed(BB2DomainInfo& bbd, const std::str
 {
   string domain=toLower(qname);
 
-  recordstorage_t::const_iterator iter = bbd.d_records->upper_bound(domain);
+  shared_ptr<const recordstorage_t> records = bbd.d_records.get();
+  recordstorage_t::const_iterator iter = records->upper_bound(domain);
 
   if (before.empty()){
     //cout<<"starting before for: '"<<domain<<"'"<<endl;
-    iter = bbd.d_records->upper_bound(domain);
+    iter = records->upper_bound(domain);
 
-    while(iter == bbd.d_records->end() || (iter->qname) > domain || (!(iter->auth) && (!(iter->qtype == QType::NS))) || (!(iter->qtype)))
+    while(iter == records->end() || (iter->qname) > domain || (!(iter->auth) && (!(iter->qtype == QType::NS))) || (!(iter->qtype)))
       iter--;
 
     before=iter->qname;
@@ -946,9 +887,9 @@ bool Bind2Backend::findBeforeAndAfterUnhashed(BB2DomainInfo& bbd, const std::str
   }
 
   //cerr<<"Now after"<<endl;
-  iter = bbd.d_records->upper_bound(domain);
+  iter = records->upper_bound(domain);
 
-  if(iter == bbd.d_records->end()) {
+  if(iter == records->end()) {
     //cerr<<"\tFound the end, begin storage: '"<<bbd.d_records->begin()->qname<<"', '"<<bbd.d_name<<"'"<<endl;
     after.clear(); // this does the right thing (i.e. point to apex, which is sure to have auth records)
   } else {
@@ -958,7 +899,7 @@ bool Bind2Backend::findBeforeAndAfterUnhashed(BB2DomainInfo& bbd, const std::str
     while((!(iter->auth) && (!(iter->qtype == QType::NS))) || (!(iter->qtype)))
     {
       iter++;
-      if(iter == bbd.d_records->end())
+      if(iter == records->end())
       {
         after.clear();
         break;
@@ -973,10 +914,12 @@ bool Bind2Backend::findBeforeAndAfterUnhashed(BB2DomainInfo& bbd, const std::str
 
 bool Bind2Backend::getBeforeAndAfterNamesAbsolute(uint32_t id, const std::string& qname, std::string& unhashed, std::string& before, std::string& after)
 {
-  shared_ptr<State> state = s_state;
-  BB2DomainInfo& bbd = state->id_zone_map[id];  
+
+  BB2DomainInfo bbd;
+  safeGetBBDomainInfo(id, &bbd);
+
   NSEC3PARAMRecordContent ns3pr;
-  string auth=state->id_zone_map[id].d_name;
+  string auth=bbd.d_name;
     
   if(!getNSEC3PARAM(auth, &ns3pr)) {
     //cerr<<"in bind2backend::getBeforeAndAfterAbsolute: no nsec3 for "<<auth<<endl;
@@ -987,7 +930,7 @@ bool Bind2Backend::getBeforeAndAfterNamesAbsolute(uint32_t id, const std::string
     string lqname = toLower(qname);
     // cerr<<"\nin bind2backend::getBeforeAndAfterAbsolute: nsec3 HASH for "<<auth<<", asked for: "<<lqname<< " (auth: "<<auth<<".)"<<endl;
     typedef recordstorage_t::index<HashedTag>::type records_by_hashindex_t;
-    records_by_hashindex_t& hashindex=boost::multi_index::get<HashedTag>(*bbd.d_records);
+    records_by_hashindex_t& hashindex=boost::multi_index::get<HashedTag>(*bbd.d_records.get());
     
 //    BOOST_FOREACH(const Bind2DNSRecord& bdr, hashindex) {
 //      cerr<<"Hash: "<<bdr.nsec3hash<<"\t"<< (lqname < bdr.nsec3hash) <<endl;
@@ -1076,7 +1019,7 @@ void Bind2Backend::lookup(const QType &qtype, const string &qname, DNSPacket *pk
   if(mustlog) 
     L<<Logger::Warning<<"Lookup for '"<<qtype.getName()<<"' of '"<<domain<<"'"<<endl;
 
-  shared_ptr<State> state = s_state;
+  shared_ptr<const State> state = s_state.get();
 
   name_id_map_t::const_iterator iditer;
   do {
@@ -1105,8 +1048,8 @@ void Bind2Backend::lookup(const QType &qtype, const string &qname, DNSPacket *pk
 
   d_handle.qtype=qtype;
   d_handle.domain=qname.substr(qname.size()-domain.length());
-
-  BB2DomainInfo& bbd = state->id_zone_map[iditer->second];
+  BB2DomainInfo bbd;
+  safeGetBBDomainInfo(iditer->second, &bbd);
   if(!bbd.d_loaded) {
     d_handle.reset();
     throw DBException("Zone for '"+bbd.d_name+"' in '"+bbd.d_filename+"' temporarily not available (file missing, or master dead)"); // fsck
@@ -1118,7 +1061,7 @@ void Bind2Backend::lookup(const QType &qtype, const string &qname, DNSPacket *pk
     throw DBException("Zone for '"+bbd.d_name+"' in '"+bbd.d_filename+"' being reloaded"); // if we don't throw here, we crash for some reason
   }
 
-  d_handle.d_records = bbd.d_records; // give it a reference counted copy
+  d_handle.d_records = bbd.d_records.get();
   
   if(d_handle.d_records->empty())
     DLOG(L<<"Query with no results"<<endl);
@@ -1226,14 +1169,14 @@ bool Bind2Backend::handle::get_normal(DNSResourceRecord &r)
 
 bool Bind2Backend::list(const string &target, int id, bool include_disabled)
 {
-  shared_ptr<State> state = s_state;
+  shared_ptr<const State> state = s_state.get();
   if(!state->id_zone_map.count(id))
     return false;
 
   d_handle.reset(); 
   DLOG(L<<"Bind2Backend constructing handle for list of "<<id<<endl);
 
-  d_handle.d_records=state->id_zone_map[id].d_records; // give it a copy, which will stay around
+  d_handle.d_records=state->id_zone_map[id].d_records.get(); // give it a copy, which will stay around
   d_handle.d_qname_iter= d_handle.d_records->begin();
   d_handle.d_qname_end=d_handle.d_records->end();   // iter now points to a vector of pointers to vector<BBResourceRecords>
 
@@ -1263,7 +1206,7 @@ bool Bind2Backend::handle::get_list(DNSResourceRecord &r)
 // this function really is too slow
 bool Bind2Backend::isMaster(const string &name, const string &ip)
 {
-  shared_ptr<State> state = getState(); 
+  shared_ptr<const State> state = s_state.get(); 
   for(id_zone_map_t::iterator j=state->id_zone_map.begin(); j!=state->id_zone_map.end();++j) {
     if(j->second.d_name==name) {
       for(vector<string>::const_iterator iter = j->second.d_masters.begin(); iter != j->second.d_masters.end(); ++iter)
@@ -1316,12 +1259,12 @@ BB2DomainInfo &Bind2Backend::createDomainEntry(const string &domain, const strin
   int newid=1;
   // Find a free zone id nr.  
   
-  if (!s_state->id_zone_map.empty()) {
-    id_zone_map_t::reverse_iterator i = s_state->id_zone_map.rbegin();
+  if (!s_state.get()->id_zone_map.empty()) {
+    id_zone_map_t::reverse_iterator i = s_state.get()->id_zone_map.rbegin();
     newid = i->second.d_id + 1;
   }
   
-  BB2DomainInfo &bbd = s_state->id_zone_map[newid];
+  BB2DomainInfo &bbd = s_state.get()->id_zone_map[newid];
 
   bbd.d_id = newid;
   bbd.d_records = shared_ptr<recordstorage_t >(new recordstorage_t);
@@ -1334,7 +1277,6 @@ BB2DomainInfo &Bind2Backend::createDomainEntry(const string &domain, const strin
 
 bool Bind2Backend::createSlaveDomain(const string &ip, const string &domain, const string &nameserver, const string &account)
 {
-
   string filename = getArg("supermaster-destdir")+'/'+domain;
   
   L << Logger::Warning << d_logprefix
@@ -1361,13 +1303,13 @@ bool Bind2Backend::createSlaveDomain(const string &ip, const string &domain, con
   }
 
   // Interference with loadConfig() and DLAddDomainHandler(), use locking
-  Lock l(&s_state_lock);
+  Lock l(&s_state.d_lock);
 
   BB2DomainInfo &bbd = createDomainEntry(canonic(domain), filename);
 
   bbd.d_masters.push_back(ip);
   
-  s_state->name_id_map[bbd.d_name] = bbd.d_id;
+  s_state.get()->name_id_map[bbd.d_name] = bbd.d_id;
 
   return true;
 }
index 713b3e426b25f7cd8cf42f58050c3534b69f822b..51ae43bff3dec649bbe2dea9bb515b8ec1e00d08 100644 (file)
@@ -36,6 +36,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include "pdns/lock.hh"
 #include "pdns/misc.hh"
 #include "pdns/dnsbackend.hh"
 
@@ -90,6 +91,55 @@ typedef multi_index_container<
               >
 > recordstorage_t;
 
+template <typename T>
+class LookButDontTouch //  : public boost::noncopyable
+{
+public:
+  LookButDontTouch() 
+  {
+    pthread_mutex_init(&d_lock, 0);
+    pthread_mutex_init(&d_swaplock, 0);
+  }
+  LookButDontTouch(shared_ptr<T> records) : d_records(records)
+  {
+    pthread_mutex_init(&d_lock, 0);
+    pthread_mutex_init(&d_swaplock, 0);
+  }
+
+  shared_ptr<const T> get()
+  {
+    shared_ptr<const T> ret;
+    {
+      Lock l(&d_lock);
+      ret = d_records;
+    }
+    return ret;
+  }
+
+  shared_ptr<T> getWRITABLE()
+  {
+    shared_ptr<T> ret;
+    {
+      Lock l(&d_lock);
+      ret = d_records;
+    }
+    return ret;
+  }
+
+
+  void swap(shared_ptr<T> records)
+  {
+    Lock l(&d_lock);
+    Lock l2(&d_swaplock);
+    d_records.swap(records);
+  }
+  pthread_mutex_t d_lock;
+  pthread_mutex_t d_swaplock;
+private:
+  shared_ptr<T> d_records;
+};
+
+
 /** Class which describes all metadata of a domain for storage by the Bind2Backend, and also contains a pointer to a vector of Bind2DNSRecord's */
 class BB2DomainInfo
 {
@@ -113,7 +163,7 @@ public:
 
   uint32_t d_lastnotified; //!< Last serial number we notified our slaves of
 
-  shared_ptr<recordstorage_t > d_records;  //!< the actual records belonging to this domain
+  LookButDontTouch<recordstorage_t> d_records;  //!< the actual records belonging to this domain
 private:
   time_t getCtime();
   time_t d_checkinterval;
@@ -167,17 +217,15 @@ public:
   // end of DNSSEC 
 
 
-  typedef map<string, int, CIStringCompare> name_id_map_t;
-  typedef map<uint32_t, BB2DomainInfo> id_zone_map_t;
+  typedef multi_index_container < BB2DomainInfo , 
+                                 indexed_by < ordered_unique<member<BB2DomainInfo, unsigned int, &BB2DomainInfo::d_id> >,
+                                              ordered_unique<member<BB2DomainInfo, std::string, &BB2DomainInfo::d_name>, CIStringCompare >
+                                              > > state_t;
+  static state_t s_state;
+  static pthread_rwlock_t s_state_lock;
 
-  struct State : public boost::noncopyable
-  {
-    name_id_map_t name_id_map;  //!< convert a name to a domain id
-    id_zone_map_t id_zone_map;
-  };
-
-  void parseZoneFile(shared_ptr<State> staging, BB2DomainInfo *bbd);
-  static void insert(shared_ptr<State> stage, int id, const string &qname, const QType &qtype, const string &content, int ttl=300, int prio=25, const std::string& hashed=string(), bool *auth=0);
+  void parseZoneFile(BB2DomainInfo *bbd);
+  static void insert(BB2DomainInfo& bbd, const string &qname, const QType &qtype, const string &content, int ttl=300, int prio=25, const std::string& hashed=string(), bool *auth=0);
   void rediscover(string *status=0);
 
   bool isMaster(const string &name, const string &ip);
@@ -190,6 +238,10 @@ public:
 
 private:
   void setupDNSSEC();
+  static bool safeGetBBDomainInfo(int id, BB2DomainInfo* bbd);
+  static bool safePutBBDomainInfo(int id, const BB2DomainInfo& bbd);
+  static bool safeGetBBDomainInfo(const std::string& name, BB2DomainInfo* bbd);
+  bool GetBBDomainInfo(int id, BB2DomainInfo** bbd);
   shared_ptr<SSQLite3> d_dnssecdb;
   bool getNSEC3PARAM(const std::string& zname, NSEC3PARAMRecordContent* ns3p);
   class handle
@@ -200,9 +252,8 @@ private:
     
     handle();
 
-    shared_ptr<recordstorage_t > d_records;
+    shared_ptr<const recordstorage_t > d_records;
     recordstorage_t::const_iterator d_iter, d_end_iter;
-
     recordstorage_t::const_iterator d_qname_iter;
     recordstorage_t::const_iterator d_qname_end;
 
@@ -222,11 +273,6 @@ private:
     handle(const handle &);
   };
 
-
-  static shared_ptr<State> s_state;
-  static pthread_mutex_t s_state_lock;               //!< lock protecting ???
-  static pthread_mutex_t s_state_swap_lock;               
-  static shared_ptr<State> getState();
   static int s_first;                                  //!< this is raised on construction to prevent multiple instances of us being generated
   static bool s_ignore_broken_records;
 
@@ -251,7 +297,7 @@ private:
   static string DLReloadNowHandler(const vector<string>&parts, Utility::pid_t ppid);
   static string DLAddDomainHandler(const vector<string>&parts, Utility::pid_t ppid);
   static void fixupAuth(shared_ptr<recordstorage_t> records);
-  static void doEmptyNonTerminals(shared_ptr<State> stage, int id, bool nsec3zone, NSEC3PARAMRecordContent ns3pr);
+  static void doEmptyNonTerminals(BB2DomainInfo& bbd, bool nsec3zone, NSEC3PARAMRecordContent ns3pr);
   void loadConfig(string *status=0);
   static void nukeZoneRecords(BB2DomainInfo *bbd);
 };