"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "modules/bindbackend/bindbackend2.cc" between
pdns-auth-4.1.13.tar.gz and pdns-auth-4.2.0.tar.gz

About: PowerDNS Authoritative Nameserver is a versatile nameserver which supports a large number of backends (that can either be plain zone files or be more dynamic in nature).

bindbackend2.cc  (pdns-auth-4.1.13):bindbackend2.cc  (pdns-auth-4.2.0)
skipping to change at line 206 skipping to change at line 206
d_transaction_id=id; d_transaction_id=id;
return false; return false;
} }
if(id == 0) { if(id == 0) {
throw DBException("domain_id 0 is invalid for this backend."); throw DBException("domain_id 0 is invalid for this backend.");
} }
d_transaction_id=id; d_transaction_id=id;
BB2DomainInfo bbd; BB2DomainInfo bbd;
if(safeGetBBDomainInfo(id, &bbd)) { if(safeGetBBDomainInfo(id, &bbd)) {
d_transaction_tmpname=bbd.d_filename+"."+itoa(random()); d_transaction_tmpname = bbd.d_filename + "XXXXXX";
d_of=std::unique_ptr<ofstream>(new ofstream(d_transaction_tmpname.c_str())); int fd = mkstemp(&d_transaction_tmpname.at(0));
if (fd == -1) {
throw DBException("Unable to create a unique temporary zonefile '"+d_trans
action_tmpname+"': "+stringerror());
return false;
}
d_of = std::unique_ptr<ofstream>(new ofstream(d_transaction_tmpname.c_str())
);
if(!*d_of) { if(!*d_of) {
unlink(d_transaction_tmpname.c_str()); unlink(d_transaction_tmpname.c_str());
close(fd);
fd = -1;
d_of.reset(); d_of.reset();
throw DBException("Unable to open temporary zonefile '"+d_transaction_tmpn ame+"': "+stringerror()); throw DBException("Unable to open temporary zonefile '"+d_transaction_tmpn ame+"': "+stringerror());
} }
close(fd);
fd = -1;
*d_of<<"; Written by PowerDNS, don't edit!"<<endl; *d_of<<"; Written by PowerDNS, don't edit!"<<endl;
*d_of<<"; Zone '"<<bbd.d_name<<"' retrieved from master "<<endl<<"; at "<<no wTime()<<endl; // insert master info here again *d_of<<"; Zone '"<<bbd.d_name<<"' retrieved from master "<<endl<<"; at "<<no wTime()<<endl; // insert master info here again
return true; return true;
} }
return false; return false;
} }
bool Bind2Backend::commitTransaction() bool Bind2Backend::commitTransaction()
skipping to change at line 254 skipping to change at line 264
// >0 = actual transaction // >0 = actual transaction
if(d_transaction_id > 0) { if(d_transaction_id > 0) {
unlink(d_transaction_tmpname.c_str()); unlink(d_transaction_tmpname.c_str());
d_of.reset(); d_of.reset();
d_transaction_id=0; d_transaction_id=0;
} }
return true; return true;
} }
bool Bind2Backend::feedRecord(const DNSResourceRecord &rr, const DNSName &ordern ame) bool Bind2Backend::feedRecord(const DNSResourceRecord &rr, const DNSName &ordern ame, bool ordernameIsNSEC3)
{ {
BB2DomainInfo bbd; BB2DomainInfo bbd;
if (!safeGetBBDomainInfo(d_transaction_id, &bbd)) if (!safeGetBBDomainInfo(d_transaction_id, &bbd))
return false; return false;
string qname; string qname;
string name = bbd.d_name.toString(); string name = bbd.d_name.toString();
if (bbd.d_name.empty()) { if (bbd.d_name.empty()) {
qname = rr.qname.toString(); qname = rr.qname.toString();
} }
skipping to change at line 366 skipping to change at line 376
di.masters=i->d_masters; di.masters=i->d_masters;
di.backend=this; di.backend=this;
domains->push_back(di); domains->push_back(di);
}; };
} }
for(DomainInfo &di : *domains) { for(DomainInfo &di : *domains) {
// do not corrupt di if domain supplied by another backend. // do not corrupt di if domain supplied by another backend.
if (di.backend != this) if (di.backend != this)
continue; continue;
this->getSOA(di.zone, soadata); try {
this->getSOA(di.zone, soadata);
} catch(...) {
continue;
}
di.serial=soadata.serial; di.serial=soadata.serial;
} }
} }
void Bind2Backend::getUnfreshSlaveInfos(vector<DomainInfo> *unfreshDomains) void Bind2Backend::getUnfreshSlaveInfos(vector<DomainInfo> *unfreshDomains)
{ {
vector<DomainInfo> domains; vector<DomainInfo> domains;
{ {
ReadLock rl(&s_state_lock); ReadLock rl(&s_state_lock);
for(state_t::const_iterator i = s_state.begin(); i != s_state.end() ; ++i) { for(state_t::const_iterator i = s_state.begin(); i != s_state.end() ; ++i) {
skipping to change at line 404 skipping to change at line 418
try { try {
getSOA(sd.zone,soadata); // we might not *have* a SOA yet getSOA(sd.zone,soadata); // we might not *have* a SOA yet
} }
catch(...){} catch(...){}
sd.serial=soadata.serial; sd.serial=soadata.serial;
if(sd.last_check+soadata.refresh < (unsigned int)time(0)) if(sd.last_check+soadata.refresh < (unsigned int)time(0))
unfreshDomains->push_back(sd); unfreshDomains->push_back(sd);
} }
} }
bool Bind2Backend::getDomainInfo(const DNSName& domain, DomainInfo &di) bool Bind2Backend::getDomainInfo(const DNSName& domain, DomainInfo &di, bool get Serial)
{ {
BB2DomainInfo bbd; BB2DomainInfo bbd;
if(!safeGetBBDomainInfo(domain, &bbd)) if(!safeGetBBDomainInfo(domain, &bbd))
return false; return false;
di.id=bbd.d_id; di.id=bbd.d_id;
di.zone=domain; di.zone=domain;
di.masters=bbd.d_masters; di.masters=bbd.d_masters;
di.last_check=bbd.d_lastcheck; di.last_check=bbd.d_lastcheck;
di.backend=this; di.backend=this;
di.kind=bbd.d_kind; di.kind=bbd.d_kind;
di.serial=0; di.serial=0;
try { if(getSerial) {
SOAData sd; try {
sd.serial=0; SOAData sd;
sd.serial=0;
getSOA(bbd.d_name,sd); // we might not *have* a SOA yet getSOA(bbd.d_name,sd); // we might not *have* a SOA yet
di.serial=sd.serial; di.serial=sd.serial;
}
catch(...){}
} }
catch(...){}
return true; return true;
} }
void Bind2Backend::alsoNotifies(const DNSName& domain, set<string> *ips) void Bind2Backend::alsoNotifies(const DNSName& domain, set<string> *ips)
{ {
// combine global list with local list // combine global list with local list
for(set<string>::iterator i = this->alsoNotify.begin(); i != this->alsoNotify. end(); i++) { for(set<string>::iterator i = this->alsoNotify.begin(); i != this->alsoNotify. end(); i++) {
(*ips).insert(*i); (*ips).insert(*i);
} }
skipping to change at line 463 skipping to change at line 479
nsec3zone=dk.getNSEC3PARAM(bbd->d_name, &ns3pr); nsec3zone=dk.getNSEC3PARAM(bbd->d_name, &ns3pr);
} else } else
nsec3zone=getNSEC3PARAM(bbd->d_name, &ns3pr); nsec3zone=getNSEC3PARAM(bbd->d_name, &ns3pr);
bbd->d_records = shared_ptr<recordstorage_t>(new recordstorage_t()); bbd->d_records = shared_ptr<recordstorage_t>(new recordstorage_t());
ZoneParserTNG zpt(bbd->d_filename, bbd->d_name, s_binddirectory); ZoneParserTNG zpt(bbd->d_filename, bbd->d_name, s_binddirectory);
DNSResourceRecord rr; DNSResourceRecord rr;
string hashed; string hashed;
while(zpt.get(rr)) { while(zpt.get(rr)) {
if(rr.qtype.getCode() == QType::NSEC || rr.qtype.getCode() == QType::NSEC3) if(rr.qtype.getCode() == QType::NSEC || rr.qtype.getCode() == QType::NSEC3 | | rr.qtype.getCode() == QType::NSEC3PARAM)
continue; // we synthesise NSECs on demand continue; // we synthesise NSECs on demand
insertRecord(*bbd, rr.qname, rr.qtype, rr.content, rr.ttl, ""); insertRecord(*bbd, rr.qname, rr.qtype, rr.content, rr.ttl, "");
} }
fixupOrderAndAuth(*bbd, nsec3zone, ns3pr); fixupOrderAndAuth(*bbd, nsec3zone, ns3pr);
doEmptyNonTerminals(*bbd, nsec3zone, ns3pr); doEmptyNonTerminals(*bbd, nsec3zone, ns3pr);
bbd->setCtime(); bbd->setCtime();
bbd->d_loaded=true; bbd->d_loaded=true;
bbd->d_checknow=false; bbd->d_checknow=false;
bbd->d_status="parsed into memory at "+nowTime(); bbd->d_status="parsed into memory at "+nowTime();
skipping to change at line 491 skipping to change at line 507
shared_ptr<recordstorage_t> records = bb2.d_records.getWRITABLE(); shared_ptr<recordstorage_t> records = bb2.d_records.getWRITABLE();
bdr.qname=qname; bdr.qname=qname;
if(bb2.d_name.empty()) if(bb2.d_name.empty())
; ;
else if(bdr.qname.isPartOf(bb2.d_name)) else if(bdr.qname.isPartOf(bb2.d_name))
bdr.qname = bdr.qname.makeRelative(bb2.d_name); bdr.qname = bdr.qname.makeRelative(bb2.d_name);
else { else {
string msg = "Trying to insert non-zone data, name='"+bdr.qname.toLogString( )+"', qtype="+qtype.getName()+", zone='"+bb2.d_name.toLogString()+"'"; string msg = "Trying to insert non-zone data, name='"+bdr.qname.toLogString( )+"', qtype="+qtype.getName()+", zone='"+bb2.d_name.toLogString()+"'";
if(s_ignore_broken_records) { if(s_ignore_broken_records) {
L<<Logger::Warning<<msg<< " ignored" << endl; g_log<<Logger::Warning<<msg<< " ignored" << endl;
return; return;
} }
else else
throw PDNSException(msg); throw PDNSException(msg);
} }
// bdr.qname.swap(bdr.qname); // bdr.qname.swap(bdr.qname);
if(!records->empty() && bdr.qname==boost::prior(records->end())->qname) if(!records->empty() && bdr.qname==boost::prior(records->end())->qname)
bdr.qname=boost::prior(records->end())->qname; bdr.qname=boost::prior(records->end())->qname;
skipping to change at line 603 skipping to change at line 619
if (stat(filename.c_str(), &buf) != 0) if (stat(filename.c_str(), &buf) != 0)
return "Unable to load zone " + domainname.toLogString() + " from " + filena me + ": " + strerror(errno); return "Unable to load zone " + domainname.toLogString() + " from " + filena me + ": " + strerror(errno);
Bind2Backend bb2; // createdomainentry needs access to our configuration Bind2Backend bb2; // createdomainentry needs access to our configuration
bbd=bb2.createDomainEntry(domainname, filename); bbd=bb2.createDomainEntry(domainname, filename);
bbd.d_filename=filename; bbd.d_filename=filename;
bbd.d_checknow=true; bbd.d_checknow=true;
bbd.d_loaded=true; bbd.d_loaded=true;
bbd.d_lastcheck=0; bbd.d_lastcheck=0;
bbd.d_status="parsing into memory"; bbd.d_status="parsing into memory";
bbd.setCtime();
safePutBBDomainInfo(bbd); safePutBBDomainInfo(bbd);
L<<Logger::Warning<<"Zone "<<domainname<< " loaded"<<endl; g_log<<Logger::Warning<<"Zone "<<domainname<< " loaded"<<endl;
return "Loaded zone " + domainname.toLogString() + " from " + filename; return "Loaded zone " + domainname.toLogString() + " from " + filename;
} }
Bind2Backend::Bind2Backend(const string &suffix, bool loadZones) Bind2Backend::Bind2Backend(const string &suffix, bool loadZones)
{ {
d_getAllDomainMetadataQuery_stmt = NULL; d_getAllDomainMetadataQuery_stmt = NULL;
d_getDomainMetadataQuery_stmt = NULL; d_getDomainMetadataQuery_stmt = NULL;
d_deleteDomainMetadataQuery_stmt = NULL; d_deleteDomainMetadataQuery_stmt = NULL;
d_insertDomainMetadataQuery_stmt = NULL; d_insertDomainMetadataQuery_stmt = NULL;
d_getDomainKeysQuery_stmt = NULL; d_getDomainKeysQuery_stmt = NULL;
skipping to change at line 630 skipping to change at line 647
d_activateDomainKeyQuery_stmt = NULL; d_activateDomainKeyQuery_stmt = NULL;
d_deactivateDomainKeyQuery_stmt = NULL; d_deactivateDomainKeyQuery_stmt = NULL;
d_getTSIGKeyQuery_stmt = NULL; d_getTSIGKeyQuery_stmt = NULL;
d_setTSIGKeyQuery_stmt = NULL; d_setTSIGKeyQuery_stmt = NULL;
d_deleteTSIGKeyQuery_stmt = NULL; d_deleteTSIGKeyQuery_stmt = NULL;
d_getTSIGKeysQuery_stmt = NULL; d_getTSIGKeysQuery_stmt = NULL;
setArgPrefix("bind"+suffix); setArgPrefix("bind"+suffix);
d_logprefix="[bind"+suffix+"backend]"; d_logprefix="[bind"+suffix+"backend]";
d_hybrid=mustDo("hybrid"); d_hybrid=mustDo("hybrid");
d_transaction_id=0;
s_ignore_broken_records=mustDo("ignore-broken-records"); s_ignore_broken_records=mustDo("ignore-broken-records");
if (!loadZones && d_hybrid) if (!loadZones && d_hybrid)
return; return;
Lock l(&s_startup_lock); Lock l(&s_startup_lock);
d_transaction_id=0;
setupDNSSEC(); setupDNSSEC();
if(!s_first) { if(!s_first) {
return; return;
} }
if(loadZones) { if(loadZones) {
loadConfig(); loadConfig();
s_first=0; s_first=0;
} }
skipping to change at line 739 skipping to change at line 756
else else
auth = bdr.auth; auth = bdr.auth;
shorter = bdr.qname; shorter = bdr.qname;
while(shorter.chopOff()) while(shorter.chopOff())
{ {
if(!qnames.count(shorter)) if(!qnames.count(shorter))
{ {
if(!(maxent)) if(!(maxent))
{ {
L<<Logger::Error<<"Zone '"<<bbd.d_name<<"' has too many empty non term inals."<<endl; g_log<<Logger::Error<<"Zone '"<<bbd.d_name<<"' has too many empty non terminals."<<endl;
return; return;
} }
if (!nonterm.count(shorter)) { if (!nonterm.count(shorter)) {
nonterm.insert(pair<DNSName, bool>(shorter, auth)); nonterm.insert(pair<DNSName, bool>(shorter, auth));
--maxent; --maxent;
} else if (auth) } else if (auth)
nonterm[shorter] = true; nonterm[shorter] = true;
} }
} }
skipping to change at line 778 skipping to change at line 795
void Bind2Backend::loadConfig(string* status) void Bind2Backend::loadConfig(string* status)
{ {
static int domain_id=1; static int domain_id=1;
if(!getArg("config").empty()) { if(!getArg("config").empty()) {
BindParser BP; BindParser BP;
try { try {
BP.parse(getArg("config")); BP.parse(getArg("config"));
} }
catch(PDNSException &ae) { catch(PDNSException &ae) {
L<<Logger::Error<<"Error parsing bind configuration: "<<ae.reason<<endl; g_log<<Logger::Error<<"Error parsing bind configuration: "<<ae.reason<<end l;
throw; throw;
} }
vector<BindDomainInfo> domains=BP.getDomains(); vector<BindDomainInfo> domains=BP.getDomains();
this->alsoNotify = BP.getAlsoNotify(); this->alsoNotify = BP.getAlsoNotify();
s_binddirectory=BP.getDirectory(); s_binddirectory=BP.getDirectory();
// ZP.setDirectory(d_binddirectory); // ZP.setDirectory(d_binddirectory);
L<<Logger::Warning<<d_logprefix<<" Parsing "<<domains.size()<<" domain(s), w ill report when done"<<endl; g_log<<Logger::Warning<<d_logprefix<<" Parsing "<<domains.size()<<" domain(s ), will report when done"<<endl;
set<DNSName> oldnames, newnames; set<DNSName> oldnames, newnames;
{ {
ReadLock rl(&s_state_lock); ReadLock rl(&s_state_lock);
for(const BB2DomainInfo& bbd : s_state) { for(const BB2DomainInfo& bbd : s_state) {
oldnames.insert(bbd.d_name); oldnames.insert(bbd.d_name);
} }
} }
int rejected=0; int rejected=0;
int newdomains=0; int newdomains=0;
skipping to change at line 816 skipping to change at line 833
i->d_ino = st.st_ino; i->d_ino = st.st_ino;
} }
} }
sort(domains.begin(), domains.end()); // put stuff in inode order sort(domains.begin(), domains.end()); // put stuff in inode order
for(vector<BindDomainInfo>::const_iterator i=domains.begin(); for(vector<BindDomainInfo>::const_iterator i=domains.begin();
i!=domains.end(); i!=domains.end();
++i) ++i)
{ {
if (!(i->hadFileDirective)) { if (!(i->hadFileDirective)) {
L<<Logger::Warning<<d_logprefix<<" Zone '"<<i->name<<"' has no 'file' directive set in "<<getArg("config")<<endl; g_log<<Logger::Warning<<d_logprefix<<" Zone '"<<i->name<<"' has no 'fi le' directive set in "<<getArg("config")<<endl;
rejected++; rejected++;
continue; continue;
} }
if(i->type == "") if(i->type == "")
L<<Logger::Notice<<d_logprefix<<" Zone '"<<i->name<<"' has no type spe cified, assuming 'native'"<<endl; g_log<<Logger::Notice<<d_logprefix<<" Zone '"<<i->name<<"' has no type specified, assuming 'native'"<<endl;
if(i->type!="master" && i->type!="slave" && i->type != "native" && i->ty pe != "") { if(i->type!="master" && i->type!="slave" && i->type != "native" && i->ty pe != "") {
L<<Logger::Warning<<d_logprefix<<" Warning! Skipping zone '"<<i->name< <"' because type '"<<i->type<<"' is invalid"<<endl; g_log<<Logger::Warning<<d_logprefix<<" Warning! Skipping zone '"<<i->n ame<<"' because type '"<<i->type<<"' is invalid"<<endl;
rejected++; rejected++;
continue; continue;
} }
BB2DomainInfo bbd; BB2DomainInfo bbd;
bool isNew = false; bool isNew = false;
if(!safeGetBBDomainInfo(i->name, &bbd)) { if(!safeGetBBDomainInfo(i->name, &bbd)) {
isNew = true; isNew = true;
bbd.d_id=domain_id++; bbd.d_id=domain_id++;
skipping to change at line 855 skipping to change at line 872
bbd.d_also_notify=i->alsoNotify; bbd.d_also_notify=i->alsoNotify;
bbd.d_kind = DomainInfo::Native; bbd.d_kind = DomainInfo::Native;
if (i->type == "master") if (i->type == "master")
bbd.d_kind = DomainInfo::Master; bbd.d_kind = DomainInfo::Master;
if (i->type == "slave") if (i->type == "slave")
bbd.d_kind = DomainInfo::Slave; bbd.d_kind = DomainInfo::Slave;
newnames.insert(bbd.d_name); newnames.insert(bbd.d_name);
if(filenameChanged || !bbd.d_loaded || !bbd.current()) { if(filenameChanged || !bbd.d_loaded || !bbd.current()) {
L<<Logger::Info<<d_logprefix<<" parsing '"<<i->name<<"' from file '"<< i->filename<<"'"<<endl; g_log<<Logger::Info<<d_logprefix<<" parsing '"<<i->name<<"' from file '"<<i->filename<<"'"<<endl;
try { try {
parseZoneFile(&bbd); parseZoneFile(&bbd);
} }
catch(PDNSException &ae) { catch(PDNSException &ae) {
ostringstream msg; ostringstream msg;
msg<<" error at "+nowTime()+" parsing '"<<i->name<<"' from file '"<< i->filename<<"': "<<ae.reason; msg<<" error at "+nowTime()+" parsing '"<<i->name<<"' from file '"<< i->filename<<"': "<<ae.reason;
if(status) if(status)
*status+=msg.str(); *status+=msg.str();
bbd.d_status=msg.str(); bbd.d_status=msg.str();
L<<Logger::Warning<<d_logprefix<<msg.str()<<endl; g_log<<Logger::Warning<<d_logprefix<<msg.str()<<endl;
rejected++; rejected++;
} }
catch(std::system_error &ae) { catch(std::system_error &ae) {
ostringstream msg; ostringstream msg;
if (ae.code().value() == ENOENT && isNew && i->type == "slave") if (ae.code().value() == ENOENT && isNew && i->type == "slave")
msg<<" error at "+nowTime()<<" no file found for new slave domain '"<<i->name<<"'. Has not been AXFR'd yet"; msg<<" error at "+nowTime()<<" no file found for new slave domain '"<<i->name<<"'. Has not been AXFR'd yet";
else else
msg<<" error at "+nowTime()+" parsing '"<<i->name<<"' from file '" <<i->filename<<"': "<<ae.what(); msg<<" error at "+nowTime()+" parsing '"<<i->name<<"' from file '" <<i->filename<<"': "<<ae.what();
if(status) if(status)
*status+=msg.str(); *status+=msg.str();
bbd.d_status=msg.str(); bbd.d_status=msg.str();
L<<Logger::Warning<<d_logprefix<<msg.str()<<endl; g_log<<Logger::Warning<<d_logprefix<<msg.str()<<endl;
rejected++;
}
catch(std::exception &ae) {
ostringstream msg;
msg<<" error at "+nowTime()+" parsing '"<<i->name<<"' from file '"<<
i->filename<<"': "<<ae.what();
if(status)
*status+=msg.str();
bbd.d_status=msg.str();
g_log<<Logger::Warning<<d_logprefix<<msg.str()<<endl;
rejected++; rejected++;
} }
safePutBBDomainInfo(bbd); safePutBBDomainInfo(bbd);
} }
} }
vector<DNSName> diff; vector<DNSName> diff;
set_difference(oldnames.begin(), oldnames.end(), newnames.begin(), newnames. end(), back_inserter(diff)); set_difference(oldnames.begin(), oldnames.end(), newnames.begin(), newnames. end(), back_inserter(diff));
unsigned int remdomains=diff.size(); unsigned int remdomains=diff.size();
skipping to change at line 907 skipping to change at line 935
// count number of entirely new domains // count number of entirely new domains
diff.clear(); diff.clear();
set_difference(newnames.begin(), newnames.end(), oldnames.begin(), oldnames. end(), back_inserter(diff)); set_difference(newnames.begin(), newnames.end(), oldnames.begin(), oldnames. end(), back_inserter(diff));
newdomains=diff.size(); newdomains=diff.size();
ostringstream msg; ostringstream msg;
msg<<" Done parsing domains, "<<rejected<<" rejected, "<<newdomains<<" new, "<<remdomains<<" removed"; msg<<" Done parsing domains, "<<rejected<<" rejected, "<<newdomains<<" new, "<<remdomains<<" removed";
if(status) if(status)
*status=msg.str(); *status=msg.str();
L<<Logger::Error<<d_logprefix<<msg.str()<<endl; g_log<<Logger::Error<<d_logprefix<<msg.str()<<endl;
} }
} }
void Bind2Backend::queueReloadAndStore(unsigned int id) void Bind2Backend::queueReloadAndStore(unsigned int id)
{ {
BB2DomainInfo bbold; BB2DomainInfo bbold;
try { try {
if(!safeGetBBDomainInfo(id, &bbold)) if(!safeGetBBDomainInfo(id, &bbold))
return; return;
BB2DomainInfo bbnew(bbold); BB2DomainInfo bbnew(bbold);
parseZoneFile(&bbnew); parseZoneFile(&bbnew);
bbnew.d_checknow=false; bbnew.d_checknow=false;
bbnew.d_wasRejectedLastReload=false; bbnew.d_wasRejectedLastReload=false;
safePutBBDomainInfo(bbnew); safePutBBDomainInfo(bbnew);
L<<Logger::Warning<<"Zone '"<<bbnew.d_name<<"' ("<<bbnew.d_filename<<") relo aded"<<endl; g_log<<Logger::Warning<<"Zone '"<<bbnew.d_name<<"' ("<<bbnew.d_filename<<") reloaded"<<endl;
} }
catch(PDNSException &ae) { catch(PDNSException &ae) {
ostringstream msg; ostringstream msg;
msg<<" error at "+nowTime()+" parsing '"<<bbold.d_name<<"' from file '"<<bbo ld.d_filename<<"': "<<ae.reason; msg<<" error at "+nowTime()+" parsing '"<<bbold.d_name<<"' from file '"<<bbo ld.d_filename<<"': "<<ae.reason;
L<<Logger::Warning<<" error parsing '"<<bbold.d_name<<"' from file '"<<bbold .d_filename<<"': "<<ae.reason<<endl; g_log<<Logger::Warning<<" error parsing '"<<bbold.d_name<<"' from file '"<<b bold.d_filename<<"': "<<ae.reason<<endl;
bbold.d_status=msg.str(); bbold.d_status=msg.str();
bbold.d_wasRejectedLastReload=true; bbold.d_wasRejectedLastReload=true;
safePutBBDomainInfo(bbold); safePutBBDomainInfo(bbold);
} }
catch(std::exception &ae) { catch(std::exception &ae) {
ostringstream msg; ostringstream msg;
msg<<" error at "+nowTime()+" parsing '"<<bbold.d_name<<"' from file '"<<bbo ld.d_filename<<"': "<<ae.what(); msg<<" error at "+nowTime()+" parsing '"<<bbold.d_name<<"' from file '"<<bbo ld.d_filename<<"': "<<ae.what();
L<<Logger::Warning<<" error parsing '"<<bbold.d_name<<"' from file '"<<bbold .d_filename<<"': "<<ae.what()<<endl; g_log<<Logger::Warning<<" error parsing '"<<bbold.d_name<<"' from file '"<<b bold.d_filename<<"': "<<ae.what()<<endl;
bbold.d_status=msg.str(); bbold.d_status=msg.str();
bbold.d_wasRejectedLastReload=true; bbold.d_wasRejectedLastReload=true;
safePutBBDomainInfo(bbold); safePutBBDomainInfo(bbold);
} }
} }
bool Bind2Backend::findBeforeAndAfterUnhashed(BB2DomainInfo& bbd, const DNSName& qname, DNSName& unhashed, DNSName& before, DNSName& after) bool Bind2Backend::findBeforeAndAfterUnhashed(BB2DomainInfo& bbd, const DNSName& qname, DNSName& unhashed, DNSName& before, DNSName& after)
{ {
shared_ptr<const recordstorage_t> records = bbd.d_records.get(); shared_ptr<const recordstorage_t> records = bbd.d_records.get();
skipping to change at line 1023 skipping to change at line 1051
} }
unhashed = iter->qname+bbd.d_name; unhashed = iter->qname+bbd.d_name;
return true; return true;
} }
} }
void Bind2Backend::lookup(const QType &qtype, const DNSName &qname, DNSPacket *p kt_p, int zoneId ) void Bind2Backend::lookup(const QType &qtype, const DNSName &qname, DNSPacket *p kt_p, int zoneId )
{ {
d_handle.reset(); d_handle.reset();
DNSName domain(qname);
static bool mustlog=::arg().mustDo("query-logging"); static bool mustlog=::arg().mustDo("query-logging");
if(mustlog)
L<<Logger::Warning<<"Lookup for '"<<qtype.getName()<<"' of '"<<domain<<"' wi bool found;
thin zoneID "<<zoneId<<endl; DNSName domain;
bool found=false;
BB2DomainInfo bbd; BB2DomainInfo bbd;
do { if(mustlog)
found = safeGetBBDomainInfo(domain, &bbd); g_log<<Logger::Warning<<"Lookup for '"<<qtype.getName()<<"' of '"<<qname<<"'
} while ((!found || (zoneId != (int)bbd.d_id && zoneId != -1)) && domain.chopO within zoneID "<<zoneId<<endl;
ff());
if (zoneId >= 0) {
if ((found = (safeGetBBDomainInfo(zoneId, &bbd) && qname.isPartOf(bbd.d_name
)))) {
domain = bbd.d_name;
}
} else {
domain = qname;
do {
found = safeGetBBDomainInfo(domain, &bbd);
} while (!found && qtype != QType::SOA && domain.chopOff());
}
if(!found) { if(!found) {
if(mustlog) if(mustlog)
L<<Logger::Warning<<"Found no authoritative zone for "<<qname<<endl; g_log<<Logger::Warning<<"Found no authoritative zone for '"<<qname<<"' and /or id "<<bbd.d_id<<endl;
d_handle.d_list=false; d_handle.d_list=false;
return; return;
} }
if(mustlog) if(mustlog)
L<<Logger::Warning<<"Found a zone '"<<domain<<"' (with id " << bbd.d_id<<") that might contain data "<<endl; g_log<<Logger::Warning<<"Found a zone '"<<domain<<"' (with id " << bbd.d_id< <") that might contain data "<<endl;
d_handle.id=bbd.d_id; d_handle.id=bbd.d_id;
d_handle.qname=qname.makeRelative(domain); // strip domain name
DLOG(L<<"Bind2Backend constructing handle for search for "<<qtype.getName()<<"
for "<<
qname<<endl);
if(domain.empty())
d_handle.qname=qname;
else if(qname.isPartOf(domain))
d_handle.qname=qname.makeRelative(domain); // strip domain name
d_handle.qtype=qtype; d_handle.qtype=qtype;
d_handle.domain=domain; d_handle.domain=domain;
if(!bbd.d_loaded) { if(!bbd.d_loaded) {
d_handle.reset(); d_handle.reset();
throw DBException("Zone for '"+bbd.d_name.toLogString()+"' in '"+bbd.d_filen ame+"' temporarily not available (file missing, or master dead)"); // fsck throw DBException("Zone for '"+bbd.d_name.toLogString()+"' in '"+bbd.d_filen ame+"' temporarily not available (file missing, or master dead)"); // fsck
} }
if(!bbd.current()) { if(!bbd.current()) {
L<<Logger::Warning<<"Zone '"<<bbd.d_name<<"' ("<<bbd.d_filename<<") needs re loading"<<endl; g_log<<Logger::Warning<<"Zone '"<<bbd.d_name<<"' ("<<bbd.d_filename<<") need s reloading"<<endl;
queueReloadAndStore(bbd.d_id); queueReloadAndStore(bbd.d_id);
if (!safeGetBBDomainInfo(domain, &bbd)) if (!safeGetBBDomainInfo(domain, &bbd))
throw DBException("Zone '"+bbd.d_name.toLogString()+"' ("+bbd.d_filename+" ) gone after reload"); // if we don't throw here, we crash for some reason throw DBException("Zone '"+bbd.d_name.toLogString()+"' ("+bbd.d_filename+" ) gone after reload"); // if we don't throw here, we crash for some reason
} }
d_handle.d_records = bbd.d_records.get(); d_handle.d_records = bbd.d_records.get();
if(d_handle.d_records->empty()) if(d_handle.d_records->empty())
DLOG(L<<"Query with no results"<<endl); DLOG(g_log<<"Query with no results"<<endl);
d_handle.mustlog = mustlog; d_handle.mustlog = mustlog;
auto& hashedidx = boost::multi_index::get<UnorderedNameTag>(*d_handle.d_record s); auto& hashedidx = boost::multi_index::get<UnorderedNameTag>(*d_handle.d_record s);
auto range = hashedidx.equal_range(d_handle.qname); auto range = hashedidx.equal_range(d_handle.qname);
if(range.first==range.second) { if(range.first==range.second) {
d_handle.d_list=false; d_handle.d_list=false;
d_handle.d_iter = d_handle.d_end_iter = range.first; d_handle.d_iter = d_handle.d_end_iter = range.first;
return; return;
skipping to change at line 1102 skipping to change at line 1131
Bind2Backend::handle::handle() Bind2Backend::handle::handle()
{ {
mustlog=false; mustlog=false;
} }
bool Bind2Backend::get(DNSResourceRecord &r) bool Bind2Backend::get(DNSResourceRecord &r)
{ {
if(!d_handle.d_records) { if(!d_handle.d_records) {
if(d_handle.mustlog) if(d_handle.mustlog)
L<<Logger::Warning<<"There were no answers"<<endl; g_log<<Logger::Warning<<"There were no answers"<<endl;
return false; return false;
} }
if(!d_handle.get(r)) { if(!d_handle.get(r)) {
if(d_handle.mustlog) if(d_handle.mustlog)
L<<Logger::Warning<<"End of answers"<<endl; g_log<<Logger::Warning<<"End of answers"<<endl;
d_handle.reset(); d_handle.reset();
return false; return false;
} }
if(d_handle.mustlog) if(d_handle.mustlog)
L<<Logger::Warning<<"Returning: '"<<r.qtype.getName()<<"' of '"<<r.qname<<"' , content: '"<<r.content<<"'"<<endl; g_log<<Logger::Warning<<"Returning: '"<<r.qtype.getName()<<"' of '"<<r.qname <<"', content: '"<<r.content<<"'"<<endl;
return true; return true;
} }
bool Bind2Backend::handle::get(DNSResourceRecord &r) bool Bind2Backend::handle::get(DNSResourceRecord &r)
{ {
if(d_list) if(d_list)
return get_list(r); return get_list(r);
else else
return get_normal(r); return get_normal(r);
} }
skipping to change at line 1137 skipping to change at line 1166
void Bind2Backend::handle::reset() void Bind2Backend::handle::reset()
{ {
d_records.reset(); d_records.reset();
qname.clear(); qname.clear();
mustlog=false; mustlog=false;
} }
//#define DLOG(x) x //#define DLOG(x) x
bool Bind2Backend::handle::get_normal(DNSResourceRecord &r) bool Bind2Backend::handle::get_normal(DNSResourceRecord &r)
{ {
DLOG(L << "Bind2Backend get() was called for "<<qtype.getName() << " record fo r '"<< DLOG(g_log << "Bind2Backend get() was called for "<<qtype.getName() << " recor d for '"<<
qname<<"' - "<<d_records->size()<<" available in total!"<<endl); qname<<"' - "<<d_records->size()<<" available in total!"<<endl);
if(d_iter==d_end_iter) { if(d_iter==d_end_iter) {
return false; return false;
} }
while(d_iter!=d_end_iter && !(qtype.getCode()==QType::ANY || (d_iter)->qtype== qtype.getCode())) { while(d_iter!=d_end_iter && !(qtype.getCode()==QType::ANY || (d_iter)->qtype== qtype.getCode())) {
DLOG(L<<Logger::Warning<<"Skipped "<<qname<<"/"<<QType(d_iter->qtype).getNam e()<<": '"<<d_iter->content<<"'"<<endl); DLOG(g_log<<Logger::Warning<<"Skipped "<<qname<<"/"<<QType(d_iter->qtype).ge tName()<<": '"<<d_iter->content<<"'"<<endl);
d_iter++; d_iter++;
} }
if(d_iter==d_end_iter) { if(d_iter==d_end_iter) {
return false; return false;
} }
DLOG(L << "Bind2Backend get() returning a rr with a "<<QType(d_iter->qtype).ge tCode()<<endl); DLOG(g_log << "Bind2Backend get() returning a rr with a "<<QType(d_iter->qtype ).getCode()<<endl);
r.qname=qname.empty() ? domain : (qname+domain); r.qname=qname.empty() ? domain : (qname+domain);
r.domain_id=id; r.domain_id=id;
r.content=(d_iter)->content; r.content=(d_iter)->content;
// r.domain_id=(d_iter)->domain_id; // r.domain_id=(d_iter)->domain_id;
r.qtype=(d_iter)->qtype; r.qtype=(d_iter)->qtype;
r.ttl=(d_iter)->ttl; r.ttl=(d_iter)->ttl;
//if(!d_iter->auth && r.qtype.getCode() != QType::A && r.qtype.getCode()!=QTyp e::AAAA && r.qtype.getCode() != QType::NS) //if(!d_iter->auth && r.qtype.getCode() != QType::A && r.qtype.getCode()!=QTyp e::AAAA && r.qtype.getCode() != QType::NS)
// cerr<<"Warning! Unauth response for qtype "<< r.qtype.getName() << " for ' "<<r.qname<<"'"<<endl; // cerr<<"Warning! Unauth response for qtype "<< r.qtype.getName() << " for ' "<<r.qname<<"'"<<endl;
skipping to change at line 1177 skipping to change at line 1206
} }
bool Bind2Backend::list(const DNSName& target, int id, bool include_disabled) bool Bind2Backend::list(const DNSName& target, int id, bool include_disabled)
{ {
BB2DomainInfo bbd; BB2DomainInfo bbd;
if(!safeGetBBDomainInfo(id, &bbd)) if(!safeGetBBDomainInfo(id, &bbd))
return false; return false;
d_handle.reset(); d_handle.reset();
DLOG(L<<"Bind2Backend constructing handle for list of "<<id<<endl); DLOG(g_log<<"Bind2Backend constructing handle for list of "<<id<<endl);
d_handle.d_records=bbd.d_records.get(); // give it a copy, which will stay aro und d_handle.d_records=bbd.d_records.get(); // give it a copy, which will stay aro und
d_handle.d_qname_iter= d_handle.d_records->begin(); d_handle.d_qname_iter= d_handle.d_records->begin();
d_handle.d_qname_end=d_handle.d_records->end(); // iter now points to a vect or of pointers to vector<BBResourceRecords> d_handle.d_qname_end=d_handle.d_records->end(); // iter now points to a vect or of pointers to vector<BBResourceRecords>
d_handle.id=id; d_handle.id=id;
d_handle.domain=bbd.d_name; d_handle.domain=bbd.d_name;
d_handle.d_list=true; d_handle.d_list=true;
return true; return true;
} }
skipping to change at line 1204 skipping to change at line 1233
r.content=(d_qname_iter)->content; r.content=(d_qname_iter)->content;
r.qtype=(d_qname_iter)->qtype; r.qtype=(d_qname_iter)->qtype;
r.ttl=(d_qname_iter)->ttl; r.ttl=(d_qname_iter)->ttl;
r.auth = d_qname_iter->auth; r.auth = d_qname_iter->auth;
d_qname_iter++; d_qname_iter++;
return true; return true;
} }
return false; return false;
} }
bool Bind2Backend::isMaster(const DNSName& name, const string &ip)
{
BB2DomainInfo bbd;
if(!safeGetBBDomainInfo(name, &bbd))
return false;
if(bbd.d_kind != DomainInfo::Slave)
return false;
for(vector<string>::const_iterator iter = bbd.d_masters.begin(); iter != bbd.d
_masters.end(); ++iter) {
try {
const ComboAddress caMaster(*iter);
if(ip == caMaster.toString()) {
return true;
}
}
catch(...) {}
}
return false;
}
bool Bind2Backend::superMasterBackend(const string &ip, const DNSName& domain, c onst vector<DNSResourceRecord>&nsset, string *nameserver, string *account, DNSBa ckend **db) bool Bind2Backend::superMasterBackend(const string &ip, const DNSName& domain, c onst vector<DNSResourceRecord>&nsset, string *nameserver, string *account, DNSBa ckend **db)
{ {
// Check whether we have a configfile available. // Check whether we have a configfile available.
if (getArg("supermaster-config").empty()) if (getArg("supermaster-config").empty())
return false; return false;
ifstream c_if(getArg("supermasters").c_str(), std::ios::in); // this was nocre ate? ifstream c_if(getArg("supermasters").c_str(), std::ios::in); // this was nocre ate?
if (!c_if) { if (!c_if) {
L << Logger::Error << "Unable to open supermasters file for read: " << strin gerror() << endl; g_log << Logger::Error << "Unable to open supermasters file for read: " << s tringerror() << endl;
return false; return false;
} }
// Format: // Format:
// <ip> <accountname> // <ip> <accountname>
string line, sip, saccount; string line, sip, saccount;
while (getline(c_if, line)) { while (getline(c_if, line)) {
std::istringstream ii(line); std::istringstream ii(line);
ii >> sip; ii >> sip;
if (sip == ip) { if (sip == ip) {
skipping to change at line 1273 skipping to change at line 1280
{ {
int newid=1; int newid=1;
{ // Find a free zone id nr. { // Find a free zone id nr.
ReadLock rl(&s_state_lock); ReadLock rl(&s_state_lock);
if (!s_state.empty()) { if (!s_state.empty()) {
newid = s_state.rbegin()->d_id+1; newid = s_state.rbegin()->d_id+1;
} }
} }
BB2DomainInfo bbd; BB2DomainInfo bbd;
bbd.d_kind = DomainInfo::Native;
bbd.d_id = newid; bbd.d_id = newid;
bbd.d_records = shared_ptr<recordstorage_t >(new recordstorage_t); bbd.d_records = shared_ptr<recordstorage_t >(new recordstorage_t);
bbd.d_name = domain; bbd.d_name = domain;
bbd.setCheckInterval(getArgAsNum("check-interval")); bbd.setCheckInterval(getArgAsNum("check-interval"));
bbd.d_filename = filename; bbd.d_filename = filename;
return bbd; return bbd;
} }
bool Bind2Backend::createSlaveDomain(const string &ip, const DNSName& domain, co nst string &nameserver, const string &account) bool Bind2Backend::createSlaveDomain(const string &ip, const DNSName& domain, co nst string &nameserver, const string &account)
{ {
string filename = getArg("supermaster-destdir")+'/'+domain.toStringNoDot(); string filename = getArg("supermaster-destdir")+'/'+domain.toStringNoDot();
L << Logger::Warning << d_logprefix g_log << Logger::Warning << d_logprefix
<< " Writing bind config zone statement for superslave zone '" << domain << " Writing bind config zone statement for superslave zone '" << domain
<< "' from supermaster " << ip << endl; << "' from supermaster " << ip << endl;
{ {
Lock l2(&s_supermaster_config_lock); Lock l2(&s_supermaster_config_lock);
ofstream c_of(getArg("supermaster-config").c_str(), std::ios::app); ofstream c_of(getArg("supermaster-config").c_str(), std::ios::app);
if (!c_of) { if (!c_of) {
L << Logger::Error << "Unable to open supermaster configfile for append: " << stringerror() << endl; g_log << Logger::Error << "Unable to open supermaster configfile for appen d: " << stringerror() << endl;
throw DBException("Unable to open supermaster configfile for append: "+str ingerror()); throw DBException("Unable to open supermaster configfile for append: "+str ingerror());
} }
c_of << endl; c_of << endl;
c_of << "# Superslave zone '" << domain.toString() << "' (added: " << nowTim e() << ") (account: " << account << ')' << endl; c_of << "# Superslave zone '" << domain.toString() << "' (added: " << nowTim e() << ") (account: " << account << ')' << endl;
c_of << "zone \"" << domain.toStringNoDot() << "\" {" << endl; c_of << "zone \"" << domain.toStringNoDot() << "\" {" << endl;
c_of << "\ttype slave;" << endl; c_of << "\ttype slave;" << endl;
c_of << "\tfile \"" << filename << "\";" << endl; c_of << "\tfile \"" << filename << "\";" << endl;
c_of << "\tmasters { " << ip << "; };" << endl; c_of << "\tmasters { " << ip << "; };" << endl;
c_of << "};" << endl; c_of << "};" << endl;
c_of.close(); c_of.close();
} }
BB2DomainInfo bbd = createDomainEntry(domain, filename); BB2DomainInfo bbd = createDomainEntry(domain, filename);
bbd.d_kind = DomainInfo::Slave; bbd.d_kind = DomainInfo::Slave;
bbd.d_masters.push_back(ip); bbd.d_masters.push_back(ComboAddress(ip, 53));
bbd.setCtime();
safePutBBDomainInfo(bbd); safePutBBDomainInfo(bbd);
return true; return true;
} }
bool Bind2Backend::searchRecords(const string &pattern, int maxResults, vector<D NSResourceRecord>& result) bool Bind2Backend::searchRecords(const string &pattern, int maxResults, vector<D NSResourceRecord>& result)
{ {
SimpleMatch sm(pattern,true); SimpleMatch sm(pattern,true);
static bool mustlog=::arg().mustDo("query-logging"); static bool mustlog=::arg().mustDo("query-logging");
if(mustlog) if(mustlog)
L<<Logger::Warning<<"Search for pattern '"<<pattern<<"'"<<endl; g_log<<Logger::Warning<<"Search for pattern '"<<pattern<<"'"<<endl;
{ {
ReadLock rl(&s_state_lock); ReadLock rl(&s_state_lock);
for(state_t::const_iterator i = s_state.begin(); i != s_state.end() ; ++i) { for(state_t::const_iterator i = s_state.begin(); i != s_state.end() ; ++i) {
BB2DomainInfo h; BB2DomainInfo h;
safeGetBBDomainInfo(i->d_id, &h); if (!safeGetBBDomainInfo(i->d_id, &h)) {
continue;
}
shared_ptr<const recordstorage_t> rhandle = h.d_records.get(); shared_ptr<const recordstorage_t> rhandle = h.d_records.get();
for(recordstorage_t::const_iterator ri = rhandle->begin(); result.size() < static_cast<vector<DNSResourceRecord>::size_type>(maxResults) && ri != rhandle- >end(); ri++) { for(recordstorage_t::const_iterator ri = rhandle->begin(); result.size() < static_cast<vector<DNSResourceRecord>::size_type>(maxResults) && ri != rhandle- >end(); ri++) {
DNSName name = ri->qname.empty() ? i->d_name : (ri->qname+i->d_name); DNSName name = ri->qname.empty() ? i->d_name : (ri->qname+i->d_name);
if (sm.match(name) || sm.match(ri->content)) { if (sm.match(name) || sm.match(ri->content)) {
DNSResourceRecord r; DNSResourceRecord r;
r.qname=name; r.qname=name;
r.domain_id=i->d_id; r.domain_id=i->d_id;
r.content=ri->content; r.content=ri->content;
r.qtype=ri->qtype; r.qtype=ri->qtype;
skipping to change at line 1363 skipping to change at line 1376
void declareArguments(const string &suffix="") void declareArguments(const string &suffix="")
{ {
declare(suffix,"ignore-broken-records","Ignore records that are out-of- bound for the zone.","no"); declare(suffix,"ignore-broken-records","Ignore records that are out-of- bound for the zone.","no");
declare(suffix,"config","Location of named.conf",""); declare(suffix,"config","Location of named.conf","");
declare(suffix,"check-interval","Interval for zonefile changes","0"); declare(suffix,"check-interval","Interval for zonefile changes","0");
declare(suffix,"supermaster-config","Location of (part of) named.conf w here pdns can write zone-statements to",""); declare(suffix,"supermaster-config","Location of (part of) named.conf w here pdns can write zone-statements to","");
declare(suffix,"supermasters","List of IP-addresses of supermasters","" ); declare(suffix,"supermasters","List of IP-addresses of supermasters","" );
declare(suffix,"supermaster-destdir","Destination directory for newly a dded slave zones",::arg()["config-dir"]); declare(suffix,"supermaster-destdir","Destination directory for newly a dded slave zones",::arg()["config-dir"]);
declare(suffix,"dnssec-db","Filename to store & access our DNSSEC metad atabase, empty for none", ""); declare(suffix,"dnssec-db","Filename to store & access our DNSSEC metad atabase, empty for none", "");
declare(suffix,"dnssec-db-journal-mode","SQLite3 journal mode", "WAL");
declare(suffix,"hybrid","Store DNSSEC metadata in other backend","no"); declare(suffix,"hybrid","Store DNSSEC metadata in other backend","no");
} }
DNSBackend *make(const string &suffix="") DNSBackend *make(const string &suffix="")
{ {
assertEmptySuffix(suffix);
return new Bind2Backend(suffix); return new Bind2Backend(suffix);
} }
DNSBackend *makeMetadataOnly(const string &suffix="") DNSBackend *makeMetadataOnly(const string &suffix="")
{ {
assertEmptySuffix(suffix);
return new Bind2Backend(suffix, false); return new Bind2Backend(suffix, false);
} }
private:
void assertEmptySuffix(const string &suffix)
{
if(suffix.length())
throw PDNSException("launch= suffixes are not supported on the bindbac
kend");
}
}; };
//! Magic class that is activated when the dynamic library is loaded //! Magic class that is activated when the dynamic library is loaded
class Bind2Loader class Bind2Loader
{ {
public: public:
Bind2Loader() Bind2Loader()
{ {
BackendMakers().report(new Bind2Factory); BackendMakers().report(new Bind2Factory);
L << Logger::Info << "[bind2backend] This is the bind backend version " << V ERSION g_log << Logger::Info << "[bind2backend] This is the bind backend version " << VERSION
#ifndef REPRODUCIBLE #ifndef REPRODUCIBLE
<< " (" __DATE__ " " __TIME__ ")" << " (" __DATE__ " " __TIME__ ")"
#endif #endif
#ifdef HAVE_SQLITE3
<< " (with bind-dnssec-db support)"
#endif
<< " reporting" << endl; << " reporting" << endl;
} }
}; };
static Bind2Loader bind2loader; static Bind2Loader bind2loader;
 End of changes. 58 change blocks. 
88 lines changed or deleted 115 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)