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

Revision 477, 26.6 KB (checked in by ahu, 9 years ago)

Move from u_intxx_t to uintxx_t typedefs, which are preferred:
 http://lists.freedesktop.org/pipermail/release-wranglers/2004-August/000926.html
 http://www.oreillynet.com/pub/a/network/2003/10/07/michael_barr.html

massive removal of windows style line-endings

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