root/trunk/pdns/pdns/backends/bind/bindbackend2.cc @ 192

Revision 192, 24.6 KB (checked in by ahu, 10 years ago)

Mark Bergsma's work on supermaster

  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1/*
2    PowerDNS Versatile Database Driven Nameserver
3    Copyright (C) 2002-2003  PowerDNS.COM BV
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License version 2 as
7    published by the Free Software Foundation;
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17*/
18// $Id: bindbackend2.cc,v 1.5 2003/09/28 18:34:07 ahu Exp $
19#include <errno.h>
20#include <string>
21#include <map>
22#include <set>
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <unistd.h>
26#include <fstream>
27#include <sstream>
28
29using namespace std;
30
31#include "dns.hh"
32#include "dnsbackend.hh"
33#include "bindbackend2.hh"
34#include "dnspacket.hh"
35
36#include "zoneparser.hh"
37#include "bindparser.hh"
38#include "logger.hh"
39#include "arguments.hh"
40#include "huffman.hh"
41#include "qtype.hh"
42#include "misc.hh"
43#include "dynlistener.hh"
44#include "lock.hh"
45using namespace std;
46
47/** new scheme of things:
48    we have zone-id map
49    a zone-id has a vector of DNSResourceRecords */
50
51map<string,int> Bind2Backend::s_name_id_map;
52map<u_int32_t,BB2DomainInfo* > Bind2Backend::s_id_zone_map;
53int Bind2Backend::s_first=1;
54pthread_mutex_t Bind2Backend::s_startup_lock=PTHREAD_MUTEX_INITIALIZER;
55pthread_mutex_t Bind2Backend::s_zonemap_lock=PTHREAD_MUTEX_INITIALIZER;
56
57/* when a query comes in, we find the most appropriate zone and answer from that */
58
59BB2DomainInfo::BB2DomainInfo()
60{
61  d_loaded=false;
62  d_last_check=0;
63  d_checknow=false;
64  d_rwlock=new pthread_rwlock_t;
65  d_status="Seen in bind configuration";
66  d_confcount=0;
67  //  cout<<"Generated a new bbdomaininfo: "<<(void*)d_rwlock<<"/"<<getpid()<<endl;
68  pthread_rwlock_init(d_rwlock,0);
69}
70
71void BB2DomainInfo::setCheckInterval(time_t seconds)
72{
73  d_checkinterval=seconds;
74}
75
76bool BB2DomainInfo::current()
77{
78  if(d_checknow)
79    return false;
80
81  if(!d_checknow && !d_checkinterval || (time(0)-d_lastcheck<d_checkinterval) || d_filename.empty())
82    return true;
83
84  return (getCtime()==d_ctime);
85}
86
87time_t BB2DomainInfo::getCtime()
88{
89  struct stat buf;
90 
91  if(d_filename.empty() || stat(d_filename.c_str(),&buf)<0)
92    return 0; 
93  d_lastcheck=time(0);
94  return buf.st_ctime;
95}
96
97void BB2DomainInfo::setCtime()
98{
99  struct stat buf;
100  if(stat(d_filename.c_str(),&buf)<0)
101    return; 
102  d_ctime=buf.st_ctime;
103}
104
105void Bind2Backend::setNotified(u_int32_t id, u_int32_t serial)
106{
107  s_id_zone_map[id]->d_lastnotified=serial;
108}
109
110void Bind2Backend::setFresh(u_int32_t domain_id)
111{
112  s_id_zone_map[domain_id]->d_last_check=time(0);
113}
114
115bool Bind2Backend::startTransaction(const string &qname, int id)
116{
117
118  BB2DomainInfo &bbd=*s_id_zone_map[d_transaction_id=id];
119  d_transaction_tmpname=bbd.d_filename+"."+itoa(random());
120  d_of=new ofstream(d_transaction_tmpname.c_str());
121  if(!*d_of) {
122    throw DBException("Unable to open temporary zonefile '"+d_transaction_tmpname+"': "+stringerror());
123    unlink(d_transaction_tmpname.c_str());
124    delete d_of;
125    d_of=0;
126  }
127 
128  *d_of<<"; Written by PowerDNS, don't edit!"<<endl;
129  *d_of<<"; Zone '"+bbd.d_name+"' retrieved from master "<<bbd.d_master<<endl<<"; at "<<nowTime()<<endl;
130  bbd.lock();
131
132  return true;
133}
134
135bool Bind2Backend::commitTransaction()
136{
137  delete d_of;
138  d_of=0;
139  if(rename(d_transaction_tmpname.c_str(),s_id_zone_map[d_transaction_id]->d_filename.c_str())<0)
140    throw DBException("Unable to commit (rename to: '"+s_id_zone_map[d_transaction_id]->d_filename+"') AXFRed zone: "+stringerror());
141
142
143  queueReload(s_id_zone_map[d_transaction_id]);
144  s_id_zone_map[d_transaction_id]->unlock();
145  d_transaction_id=0;
146
147  return true;
148}
149
150bool Bind2Backend::abortTransaction()
151{
152  if(d_transaction_id) {
153    s_id_zone_map[d_transaction_id]->unlock();
154    delete d_of;
155    d_of=0;
156    unlink(d_transaction_tmpname.c_str());
157    d_transaction_id=0;
158  }
159
160  return true;
161}
162
163bool Bind2Backend::feedRecord(const DNSResourceRecord &r)
164{
165  string qname=r.qname;
166  string domain=s_id_zone_map[d_transaction_id]->d_name;
167
168  if(!stripDomainSuffix(&qname,domain)) 
169    throw DBException("out-of-zone data '"+qname+"' during AXFR of zone '"+domain+"'");
170
171  string content=r.content;
172
173  // SOA needs stripping too! XXX FIXME
174  switch(r.qtype.getCode()) {
175  case QType::TXT:
176    *d_of<<qname<<"\t"<<r.ttl<<"\t"<<r.qtype.getName()<<"\t\""<<r.content<<"\""<<endl;
177    break;
178  case QType::MX:
179    if(!stripDomainSuffix(&content,domain))
180      content+=".";
181    *d_of<<qname<<"\t"<<r.ttl<<"\t"<<r.qtype.getName()<<"\t"<<r.priority<<"\t"<<content<<endl;
182    break;
183  case QType::CNAME:
184  case QType::NS:
185    if(!stripDomainSuffix(&content,domain))
186      content+=".";
187    *d_of<<qname<<"\t"<<r.ttl<<"\t"<<r.qtype.getName()<<"\t"<<content<<endl;
188    break;
189  default:
190    *d_of<<qname<<"\t"<<r.ttl<<"\t"<<r.qtype.getName()<<"\t"<<r.content<<endl;
191    break;
192  }
193
194  return true;
195}
196
197void Bind2Backend::getUpdatedMasters(vector<DomainInfo> *changedDomains)
198{
199  SOAData soadata;
200  for(map<u_int32_t,BB2DomainInfo*>::iterator i=s_id_zone_map.begin();i!=s_id_zone_map.end();++i) {
201    if(!i->second->d_master.empty())
202      continue;
203    soadata.serial=0;
204    try {
205      getSOA(i->second->d_name,soadata); // we might not *have* a SOA yet
206    }
207    catch(...){}
208    DomainInfo di;
209    di.id=i->first;
210    di.serial=soadata.serial;
211    di.zone=i->second->d_name;
212    di.last_check=i->second->d_last_check;
213    di.backend=this;
214    di.kind=DomainInfo::Master;
215    if(!i->second->d_lastnotified)            // don't do notification storm on startup
216      i->second->d_lastnotified=soadata.serial;
217    else
218      if(soadata.serial!=i->second->d_lastnotified)
219        changedDomains->push_back(di);
220   
221  }
222}
223
224void Bind2Backend::getUnfreshSlaveInfos(vector<DomainInfo> *unfreshDomains)
225{
226  for(map<u_int32_t,BB2DomainInfo*>::const_iterator i=s_id_zone_map.begin();i!=s_id_zone_map.end();++i) {
227    if(i->second->d_master.empty())
228      continue;
229    DomainInfo sd;
230    sd.id=i->first;
231    sd.zone=i->second->d_name;
232    sd.master=i->second->d_master;
233    sd.last_check=i->second->d_last_check;
234    sd.backend=this;
235    sd.kind=DomainInfo::Slave;
236    SOAData soadata;
237    soadata.serial=0;
238    soadata.refresh=0;
239    soadata.serial=0;
240    soadata.db=(DNSBackend *)-1; // not sure if this is useful, inhibits any caches that might be around
241    try {
242      getSOA(i->second->d_name,soadata); // we might not *have* a SOA yet
243    }
244    catch(...){}
245    sd.serial=soadata.serial;
246    if(sd.last_check+soadata.refresh<(unsigned int)time(0))
247      unfreshDomains->push_back(sd);   
248  }
249}
250
251bool Bind2Backend::getDomainInfo(const string &domain, DomainInfo &di)
252{
253  for(map<u_int32_t,BB2DomainInfo*>::const_iterator i=s_id_zone_map.begin();i!=s_id_zone_map.end();++i) {
254    if(i->second->d_name==domain) {
255      di.id=i->first;
256      di.zone=domain;
257      di.master=i->second->d_master;
258      di.last_check=i->second->d_last_check;
259      di.backend=this;
260      di.kind=i->second->d_master.empty() ? DomainInfo::Master : DomainInfo::Slave;
261      di.serial=0;
262      try {
263        SOAData sd;
264        sd.serial=0;
265       
266        getSOA(i->second->d_name,sd); // we might not *have* a SOA yet
267        di.serial=sd.serial;
268      }
269      catch(...){}
270
271      return true;
272    }
273  }
274  return false;
275}
276
277
278static string canonic(string ret)
279{
280  string::iterator i;
281
282  for(i=ret.begin();
283      i!=ret.end();
284      ++i)
285    ; // *i=*i; //tolower(*i);
286
287
288  if(*(i-1)=='.')
289    ret.resize(i-ret.begin()-1);
290  return ret;
291}
292
293map<unsigned int, BB2DomainInfo*> nbbds;
294/** This function adds a record to a domain with a certain id. */
295void Bind2Backend::insert(int id, const string &qnameu, const string &qtype, const string &content, int ttl=300, int prio=25)
296{
297  DNSResourceRecord rr;
298  rr.domain_id=id;
299  rr.qname=canonic(qnameu);
300  rr.qtype=qtype;
301  rr.content=content;
302  rr.ttl=ttl;
303  rr.priority=prio;
304  nbbds[id]->d_records->push_back(rr);
305}
306
307
308static Bind2Backend *us;
309
310void Bind2Backend::reload()
311{
312  for(map<u_int32_t,BB2DomainInfo*>::iterator i=us->s_id_zone_map.begin();i!=us->s_id_zone_map.end();++i) 
313    i->second->d_checknow=true;
314}
315
316string Bind2Backend::DLReloadNowHandler(const vector<string>&parts, Utility::pid_t ppid)
317{
318  ostringstream ret;
319  bool doReload=false;
320  for(map<u_int32_t,BB2DomainInfo*>::iterator j=us->s_id_zone_map.begin();j!=us->s_id_zone_map.end();++j) {
321    doReload=false;
322    if(parts.size()==1)
323      doReload=true;
324    else 
325      for(vector<string>::const_iterator i=parts.begin()+1;i<parts.end();++i)                 // O(N) badness XXX FIXME
326        if(*i==j->second->d_name) {
327          doReload=true;
328          break;
329        }
330   
331    if(doReload) {
332      j->second->lock();
333
334      us->queueReload(j->second);
335      j->second->unlock();
336      ret<<j->second->d_name<< (j->second->d_loaded ? "": "[rejected]") <<"\t"<<j->second->d_status<<"\n";
337    }
338    doReload=false;
339  }
340       
341  return ret.str();
342}
343
344
345string Bind2Backend::DLDomStatusHandler(const vector<string>&parts, Utility::pid_t ppid)
346{
347  string ret;
348  bool doPrint=false;
349  for(map<u_int32_t,BB2DomainInfo*>::iterator j=us->s_id_zone_map.begin();j!=us->s_id_zone_map.end();++j) {
350    ostringstream line;
351    doPrint=false;
352    if(parts.size()==1)
353      doPrint=true;
354    else 
355      for(vector<string>::const_iterator i=parts.begin()+1;i<parts.end();++i)                 // O(N) badness XXX FIXME
356        if(*i==j->second->d_name) {
357          doPrint=true;
358          break;
359        }
360   
361    if(doPrint)
362      line<<j->second->d_name<< (j->second->d_loaded ? "": "[rejected]") <<"\t"<<j->second->d_status<<"\n";
363    doPrint=false;
364    ret+=line.str();
365  }
366       
367  return ret;
368}
369
370
371string Bind2Backend::DLListRejectsHandler(const vector<string>&parts, Utility::pid_t ppid)
372{
373  ostringstream ret;
374  for(map<u_int32_t,BB2DomainInfo*>::iterator j=us->s_id_zone_map.begin();j!=us->s_id_zone_map.end();++j) 
375    if(!j->second->d_loaded)
376      ret<<j->second->d_name<<"\t"<<j->second->d_status<<endl;
377       
378  return ret.str();
379}
380
381static void callback(unsigned int domain_id, const string &domain, const string &qtype, const string &content, int ttl, int prio)
382{
383  us->insert(domain_id,domain,qtype,content,ttl,prio);
384}
385
386
387
388Bind2Backend::Bind2Backend(const string &suffix)
389{
390#if __GNUC__ >= 3
391    ios_base::sync_with_stdio(false);
392#endif
393  d_logprefix="[bind2"+suffix+"backend]";
394  setArgPrefix("bind2"+suffix);
395  Lock l(&s_startup_lock);
396
397  d_transaction_id=0;
398  if(!s_first) {
399    return;
400  }
401   
402  s_first=0;
403 
404  if(mustDo("example-zones")) {
405    insert(0,"www.example.com","A","1.2.3.4");
406    insert(0,"example.com","SOA","ns1.example.com hostmaster.example.com");
407    insert(0,"example.com","NS","ns1.example.com",86400);
408    insert(0,"example.com","NS","ns2.example.com",86400);
409    insert(0,"example.com","MX","mail.example.com",3600,25);
410    insert(0,"example.com","MX","mail1.example.com",3600,25);
411    insert(0,"mail.example.com","A","4.3.2.1");
412    insert(0,"mail1.example.com","A","5.4.3.2");
413    insert(0,"ns1.example.com","A","4.3.2.1");
414    insert(0,"ns2.example.com","A","5.4.3.2");
415     
416    for(int i=0;i<1000;i++)
417      insert(0,"host-"+itoa(i)+".example.com","A","2.3.4.5");
418
419    s_id_zone_map[0]=new BB2DomainInfo;
420    BB2DomainInfo &bbd=*s_id_zone_map[0];
421    bbd.d_name="example.com";
422    bbd.d_filename="";
423    bbd.d_id=0;
424    s_id_zone_map[0]->d_loaded=true;
425    s_id_zone_map[0]->d_status="parsed into memory at "+nowTime();
426  }
427 
428  loadConfig();
429 
430
431  extern DynListener *dl;
432  us=this;
433  dl->registerFunc("BIND2-RELOAD-NOW", &DLReloadNowHandler);
434  dl->registerFunc("BIND2-DOMAIN-STATUS", &DLDomStatusHandler);
435  dl->registerFunc("BIND2-LIST-REJECTS", &DLListRejectsHandler);
436}
437
438
439void Bind2Backend::rediscover(string *status)
440{
441  loadConfig(status);
442}
443
444void Bind2Backend::loadConfig(string* status)
445{
446  // Interference with createSlaveDomain()
447  Lock l(&s_zonemap_lock);
448 
449  static int domain_id=1;
450  nbbds.clear();
451  if(!getArg("config").empty()) {
452    BindParser BP;
453    try {
454      BP.parse(getArg("config"));
455    }
456    catch(AhuException &ae) {
457      L<<Logger::Error<<"Error parsing bind configuration: "<<ae.reason<<endl;
458      throw;
459    }
460   
461    ZoneParser ZP;
462     
463    vector<BindDomainInfo> domains=BP.getDomains();
464   
465    us=this;
466
467    ZP.setDirectory(BP.getDirectory());
468    ZP.setCallback(&callback); 
469    L<<Logger::Warning<<d_logprefix<<" Parsing "<<domains.size()<<" domain(s), will report when done"<<endl;
470   
471    int rejected=0;
472    int newdomains=0;
473
474    for(vector<BindDomainInfo>::const_iterator i=domains.begin();
475        i!=domains.end();
476        ++i)
477      {
478        BB2DomainInfo *bbd=0;
479        if(i->type!="master" && i->type!="slave") {
480          L<<Logger::Warning<<d_logprefix<<" Warning! Skipping '"<<i->type<<"' zone '"<<i->name<<"'"<<endl;
481          continue;
482        }
483        map<unsigned int, BB2DomainInfo*>::const_iterator j=s_id_zone_map.begin();
484        for(;j!=s_id_zone_map.end();++j)
485          if(j->second->d_name==i->name) {
486            bbd=j->second;
487            break;
488          }
489        if(j==s_id_zone_map.end()) { // entirely new
490          bbd=new BB2DomainInfo;
491          bbd->d_records=new vector<DNSResourceRecord>;
492         
493          bbd->d_id=domain_id++;
494          s_name_id_map[i->name]=bbd->d_id;
495          bbd->setCheckInterval(getArgAsNum("check-interval"));
496          bbd->d_lastnotified=0;
497          bbd->d_loaded=false;
498        }
499
500        bbd->d_name=i->name;
501        bbd->d_filename=i->filename;
502        bbd->d_master=i->master;
503       
504        nbbds[bbd->d_id]=bbd; 
505        if(!bbd->d_loaded) {
506          L<<Logger::Info<<d_logprefix<<" parsing '"<<i->name<<"' from file '"<<i->filename<<"'"<<endl;
507         
508          try {
509            ZP.parse(i->filename,i->name,bbd->d_id); // calls callback for us
510            nbbds[bbd->d_id]->setCtime();
511            nbbds[bbd->d_id]->d_loaded=true;          // does this perform locking for us?
512            nbbds[bbd->d_id]->d_status="parsed into memory at "+nowTime();
513           
514          }
515          catch(AhuException &ae) {
516            ostringstream msg;
517            msg<<" error at "+nowTime()+" parsing '"<<i->name<<"' from file '"<<i->filename<<"': "<<ae.reason;
518
519            if(status)
520              *status+=msg.str();
521            nbbds[bbd->d_id]->d_status=msg.str();
522            L<<Logger::Warning<<d_logprefix<<msg.str()<<endl;
523            rejected++;
524          }
525        }
526        /*
527        vector<vector<BBResourceRecord> *>&tmp=d_zone_id_map[bbd.d_id];  // shrink trick
528        vector<vector<BBResourceRecord> *>(tmp).swap(tmp);
529        */
530      }
531
532
533    int remdomains=0;
534    set<string> oldnames, newnames;
535    for(map<unsigned int, BB2DomainInfo*>::const_iterator j=s_id_zone_map.begin();j!=s_id_zone_map.end();++j) {
536      oldnames.insert(j->second->d_name);
537    }
538    for(map<unsigned int, BB2DomainInfo*>::const_iterator j=nbbds.begin();j!=nbbds.end();++j) {
539      newnames.insert(j->second->d_name);
540    }
541
542    vector<string> diff;
543    set_difference(oldnames.begin(), oldnames.end(), newnames.begin(), newnames.end(), back_inserter(diff));
544    remdomains=diff.size();
545    for(vector<string>::const_iterator k=diff.begin();k!=diff.end();++k) {
546      delete s_id_zone_map[s_name_id_map[*k]]->d_records;
547      delete s_id_zone_map[s_name_id_map[*k]];
548      s_name_id_map.erase(*k);
549      L<<Logger::Error<<"Removed: "<<*k<<endl;
550    }
551
552    for(map<unsigned int, BB2DomainInfo*>::iterator j=s_id_zone_map.begin();j!=s_id_zone_map.end();++j) { // O(N*M)
553      for(vector<string>::const_iterator k=diff.begin();k!=diff.end();++k)
554        if(j->second->d_name==*k) {
555          L<<Logger::Error<<"Removing records from zone '"<<j->second->d_name<<"' from memory"<<endl;
556          j->second->lock();
557          j->second->d_loaded=false;
558          nukeZoneRecords(j->second);
559          j->second->unlock(); 
560          break;
561        }
562    }
563
564    vector<string> diff2;
565    set_difference(newnames.begin(), newnames.end(), oldnames.begin(), oldnames.end(), back_inserter(diff2));
566    newdomains=diff2.size();
567
568    s_id_zone_map.swap(nbbds); // commit
569    ostringstream msg;
570    msg<<" Done parsing domains, "<<rejected<<" rejected, "<<newdomains<<" new, "<<remdomains<<" removed"; 
571    if(status)
572      *status=msg.str();
573
574    L<<Logger::Error<<d_logprefix<<msg.str()<<endl;
575    //   L<<Logger::Info<<d_logprefix<<" Number of hash buckets: "<<d_qnames.bucket_count()<<", number of entries: "<<d_qnames.size()<< endl;
576  }
577}
578
579/** nuke all records from memory, keep bbd intact though. Must be called with bbd lock held already! */
580void Bind2Backend::nukeZoneRecords(BB2DomainInfo *bbd)
581{
582  bbd->d_loaded=0; // block further access
583  bbd->d_records->clear();
584}
585
586/** Must be called with bbd locked already. Will not be unlocked on return, is your own problem.
587    Does not throw errors or anything, may update d_status however */
588
589
590void Bind2Backend::queueReload(BB2DomainInfo *bbd)
591{
592  nbbds.clear();
593
594  // we reload *now* for the time being
595  //cout<<"unlock domain"<<endl;
596  bbd->unlock();
597  //cout<<"lock it again"<<endl;
598
599  bbd->lock();
600  try {
601    nukeZoneRecords(bbd);
602   
603    ZoneParser ZP;
604    us=this;
605    ZP.setCallback(&callback); 
606
607    nbbds[bbd->d_id]=s_id_zone_map[bbd->d_id];
608    ZP.parse(bbd->d_filename,bbd->d_name,bbd->d_id);
609    s_id_zone_map[bbd->d_id]=nbbds[bbd->d_id];
610
611    bbd->setCtime();
612    // and raise d_loaded again!
613    bbd->d_loaded=1;
614    bbd->d_checknow=0;
615    bbd->d_status="parsed into memory at "+nowTime();
616    L<<Logger::Warning<<"Zone '"<<bbd->d_name<<"' ("<<bbd->d_filename<<") reloaded"<<endl;
617  }
618  catch(AhuException &ae) {
619    ostringstream msg;
620    msg<<" error at "+nowTime()+" parsing '"<<bbd->d_name<<"' from file '"<<bbd->d_filename<<"': "<<ae.reason;
621    bbd->d_status=msg.str();
622  }
623}
624
625void Bind2Backend::lookup(const QType &qtype,const string &qname, DNSPacket *pkt_p, int zoneId )
626{
627  d_handle=new Bind2Backend::handle;
628  d_handle->d_records=new vector<DNSResourceRecord>; // WRONG
629  //  cout<<"Lookup! for "<<qname<<endl;
630  string domain=qname;
631  while(!s_name_id_map.count(domain) && chopOff(domain));
632
633  if(!s_name_id_map.count(domain)) {
634    //    cout<<"no such domain: '"<<qname<<"'"<<endl;
635    d_handle->d_iter=d_handle->d_records->end();  // WRONG
636    d_handle->d_list=false;
637    d_handle->d_bbd=0;
638    return;
639  }
640  unsigned int id=s_name_id_map[domain];
641
642  //  cout<<"domain: '"<<domain<<"', id="<<id<<endl;
643 
644
645  DLOG(L<<"Bind2Backend constructing handle for search for "<<qtype.getName()<<" for "<<
646       qname<<endl);
647
648  d_handle->qname=qname;
649  d_handle->parent=this;
650  d_handle->qtype=qtype;
651  string compressed=toLower(qname);
652  d_handle->d_records=s_id_zone_map[id]->d_records;
653  d_handle->d_bbd=0;
654  if(!d_handle->d_records->empty()) {
655    BB2DomainInfo& bbd=*s_id_zone_map[id];
656    if(!bbd.d_loaded) {
657      delete d_handle;
658      throw DBException("Zone temporarily not available (file missing, or master dead)"); // fuck
659    }
660
661    if(!bbd.tryRLock()) {
662      L<<Logger::Warning<<"Can't get read lock on zone '"<<bbd.d_name<<"'"<<endl;
663      delete d_handle;
664      throw DBException("Temporarily unavailable due to a zone lock"); // fuck
665    }
666     
667
668    if(!bbd.current()) {
669      L<<Logger::Warning<<"Zone '"<<bbd.d_name<<"' ("<<bbd.d_filename<<") needs reloading"<<endl;
670      queueReload(&bbd);
671    }
672    d_handle->d_bbd=&bbd;
673  }
674  else {
675    DLOG(L<<"Query with no results"<<endl);
676  }
677  d_handle->d_iter=d_handle->d_records->begin();
678  d_handle->d_list=false;
679}
680
681Bind2Backend::handle::handle()
682{
683  d_bbd=0;
684  count=0;
685}
686
687bool Bind2Backend::get(DNSResourceRecord &r)
688{
689  if(!d_handle->get(r)) {
690    delete d_handle;
691    d_handle=0;
692    return false;
693  }
694  return true;
695}
696
697bool Bind2Backend::handle::get(DNSResourceRecord &r)
698{
699  if(d_list)
700    return get_list(r);
701  else
702    return get_normal(r);
703}
704
705bool Bind2Backend::handle::get_normal(DNSResourceRecord &r)
706{
707  DLOG(L << "Bind2Backend get() was called for "<<qtype.getName() << " record  for "<<
708       qname<<"- "<<d_records->size()<<" available!"<<endl);
709 
710  while(d_iter!=d_records->end() && (d_iter->qname!=qname || !(qtype=="ANY" || (d_iter)->qtype==qtype))) {
711    DLOG(L<<Logger::Warning<<"Skipped "<<qname<<"/"<<QType(d_iter->qtype).getName()<<": '"<<d_iter->content<<"'"<<endl);
712    d_iter++;
713  }
714  if(d_iter==d_records->end()) { // we've reached the end
715    if(d_bbd) {
716      d_bbd->unlock();
717      d_bbd=0;
718    }
719    return false;
720  }
721  DLOG(L << "Bind2Backend get() returning a rr with a "<<QType(d_iter->qtype).getCode()<<endl);
722
723  r.qname=qname; // fill this in
724 
725  r.content=(d_iter)->content;
726  r.domain_id=(d_iter)->domain_id;
727  r.qtype=(d_iter)->qtype;
728  r.ttl=(d_iter)->ttl;
729  r.priority=(d_iter)->priority;
730  d_iter++;
731
732  return true;
733}
734
735bool Bind2Backend::list(const string &target, int id)
736{
737  // cout<<"List of id "<<id<<" requested"<<endl;
738  if(!s_id_zone_map.count(id))
739    return false;
740
741  d_handle=new Bind2Backend::handle;
742  DLOG(L<<"Bind2Backend constructing handle for list of "<<id<<endl);
743
744  d_handle->d_qname_iter=s_id_zone_map[id]->d_records->begin();
745  d_handle->d_qname_end=s_id_zone_map[id]->d_records->end();   // iter now points to a vector of pointers to vector<BBResourceRecords>
746
747  d_handle->parent=this;
748  d_handle->id=id;
749  d_handle->d_list=true;
750  return true;
751
752}
753
754bool Bind2Backend::handle::get_list(DNSResourceRecord &r)
755{
756  if(d_qname_iter!=d_qname_end) {
757    r=*d_qname_iter;
758    d_qname_iter++;
759    return true;
760  }
761  return false;
762
763}
764
765bool Bind2Backend::isMaster(const string &name, const string &ip)
766{
767  for(map<u_int32_t,BB2DomainInfo*>::iterator j=us->s_id_zone_map.begin();j!=us->s_id_zone_map.end();++j) 
768    if(j->second->d_name==name)
769      return j->second->d_master==ip;
770  return false;
771}
772
773bool Bind2Backend::superMasterBackend(const string &ip, const string &domain, const vector<DNSResourceRecord>&nsset, string *account, DNSBackend **db)
774{
775  // Check whether we have a configfile available.
776  if (getArg("supermaster-config").empty())
777    return false;
778
779  ifstream c_if(getArg("supermasters").c_str(), ios_base::in); // this was nocreate?
780  if (!c_if) {
781    L << Logger::Error << "Unable to open supermasters file for read: " << stringerror() << endl;
782    return false;
783  }
784
785  // Format:
786  // <ip> <accountname>
787  string line, sip, saccount;
788  while (getline(c_if, line)) {
789    istringstream ii(line);
790    ii >> sip;
791    if (sip == ip) {
792      ii >> saccount;
793      break;
794    }
795  } 
796  c_if.close();
797
798  if (sip != ip)  // ip not found in authorization list - reject
799    return false;
800 
801  // ip authorized as supermaster - accept
802  *db = this;
803  if (saccount.length() > 0)
804      *account = saccount.c_str();
805
806  return true;
807}
808
809bool Bind2Backend::createSlaveDomain(const string &ip, const string &domain, const string &account)
810{
811  // Interference with loadConfig(), use locking
812  Lock l(&s_zonemap_lock);
813
814  string filename = getArg("supermaster-destdir")+'/'+domain;
815 
816  L << Logger::Warning << d_logprefix
817    << " Writing bind config zone statement for superslave zone '" << domain
818    << "' from supermaster " << ip << endl;
819       
820  ofstream c_of(getArg("supermaster-config").c_str(),  ios_base::app);
821  if (!c_of) {
822    L << Logger::Error << "Unable to open supermaster configfile for append: " << stringerror() << endl;
823    throw DBException("Unable to open supermaster configfile for append: "+stringerror());
824  }
825 
826  c_of << endl;
827  c_of << "# Superslave zone " << domain << " (added: " << nowTime() << ") (account: " << account << ')' << endl;
828  c_of << "zone \"" << domain << "\" {" << endl;
829  c_of << "\ttype slave;" << endl;
830  c_of << "\tfile \"" << filename << "\";" << endl;
831  c_of << "\tmasters { " << ip << "; };" << endl;
832  c_of << "};" << endl;
833  c_of.close();
834 
835  BB2DomainInfo *bbd = new BB2DomainInfo;
836
837  bbd->d_records = new vector<DNSResourceRecord>;
838  bbd->d_name = domain;
839  bbd->setCheckInterval(getArgAsNum("check-interval"));
840  bbd->d_master = ip;
841  bbd->d_filename = filename;
842
843  // Find a free zone id nr.   
844  if (!s_id_zone_map.empty()) {
845    map<unsigned int, BB2DomainInfo*>::reverse_iterator i = s_id_zone_map.rbegin();
846    bbd->d_id = i->second->d_id + 1;
847  }
848  else
849    bbd->d_id = 0;
850
851  s_id_zone_map[bbd->d_id] = bbd;
852  s_name_id_map[domain] = bbd->d_id;
853 
854  return true;
855}
856
857class Bind2Factory : public BackendFactory
858{
859   public:
860      Bind2Factory() : BackendFactory("bind2") {}
861
862      void declareArguments(const string &suffix="")
863      {
864         declare(suffix,"config","Location of named.conf","");
865         declare(suffix,"example-zones","Install example zones","no");
866         declare(suffix,"check-interval","Interval for zonefile changes","0");
867         declare(suffix,"supermaster-config","Location of (part of) named.conf where pdns can write zone-statements to","");
868         declare(suffix,"supermasters","List of IP-addresses of supermasters","");
869         declare(suffix,"supermaster-destdir","Destination directory for newly added slave zones",arg()["config-dir"]);
870      }
871
872      DNSBackend *make(const string &suffix="")
873      {
874         return new Bind2Backend(suffix);
875      }
876};
877
878//! Magic class that is activated when the dynamic library is loaded
879class Bind2Loader
880{
881public:
882  Bind2Loader()
883  {
884    BackendMakers().report(new Bind2Factory);
885    L<<Logger::Notice<<"[Bind2Backend] This is the bind backend version "VERSION" ("__DATE__", "__TIME__") reporting"<<endl;
886  }
887};
888static Bind2Loader bind2loader;
Note: See TracBrowser for help on using the browser.