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

Revision 1057, 30.3 KB (checked in by ahu, 6 years ago)

fix small issues in root-server operation, thanks jj

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