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

Revision 481, 26.0 KB (checked in by ahu, 8 years ago)

improve locking

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