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

Revision 479, 26.7 KB (checked in by ahu, 8 years ago)

add some whitespace, add comments to help me understand

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