AM_CXXFLAGS=-DSYSCONFDIR=\"@sysconfdir@\" -DLIBDIR=\"@libdir@\" -DLOCALSTATEDIR=\"@socketdir@\" -Ibackends/bind @THREADFLAGS@ $(LUA_CFLAGS) -Iext/polarssl-1.1.1/include
AM_CPPFLAGS=-Ibackends/bind $(BOOST_CPPFLAGS) @THREADFLAGS@
-EXTRA_DIST = dnslabeltext.rl dnslabeltext.cc mtasker.cc inflighter.cc docs/pdns_control.8 docs/pdns_server.8 docs/zone2sql.8 docs/zone2ldap.8 docs/pdnssec.8
+EXTRA_DIST = dnslabeltext.rl dnslabeltext.cc mtasker.cc inflighter.cc docs/pdns_control.8 \
+ docs/pdns_server.8 docs/zone2sql.8 docs/zone2ldap.8 docs/pdnssec.8
SUBDIRS= ext/polarssl-1.1.1 backends
resolver.hh resolver.cc slavecommunicator.cc mastercommunicator.cc communicator.cc communicator.hh dnsproxy.cc \
dnsproxy.hh randombackend.cc unix_utility.cc common_startup.cc \
utility.hh iputils.hh common_startup.hh unix_semaphore.cc \
-backends/bind/bindbackend2.cc \
+backends/bind/bindbackend2.cc backends/bind/binddnssec.cc \
backends/bind/bindparser.cc backends/bind/bindlexer.c \
backends/gsql/gsqlbackend.cc \
backends/gsql/gsqlbackend.hh backends/gsql/ssql.hh \
misc.cc misc.hh rcpgenerator.cc rcpgenerator.hh base64.cc base64.hh unix_utility.cc \
logger.cc statbag.cc qtype.cc sillyrecords.cc nsecrecords.cc dnssecinfra.cc dnssecinfra.hh \
base32.cc ueberbackend.cc dnsbackend.cc arguments.cc packetcache.cc dnspacket.cc \
- backends/bind/bindbackend2.cc \
+ backends/bind/bindbackend2.cc backends/bind/binddnssec.cc \
backends/bind/bindparser.cc backends/bind/bindlexer.c \
backends/gsql/gsqlbackend.cc \
backends/gsql/gsqlbackend.hh backends/gsql/ssql.hh zoneparser-tng.cc \
dynlistener.cc dns.cc randombackend.cc dnssecsigner.cc polarrsakeyinfra.cc md5.cc \
- signingpipe.cc dnslabeltext.cc ednssubnet.cc cachecleaner.hh
+ signingpipe.cc dnslabeltext.cc ednssubnet.cc cachecleaner.hh bind-dnssec.schema.sqlite3.sql.h
+
+bind-dnssec.schema.sqlite3.sql.h: bind-dnssec.schema.sqlite3.sql
+ ( echo 'static char sqlCreate[]=' ; sed 's/$$/"/g' bind-dnssec.schema.sqlite3.sql | sed 's/^/"/g' ; echo ';' ) > $@
pdnssec_LDFLAGS=@moduleobjects@ @modulelibs@ @DYNLINKFLAGS@ @LIBDL@ @THREADFLAGS@ $(BOOST_PROGRAM_OPTIONS_LDFLAGS) $(BOOST_SERIALIZATION_LDFLAGS)
pdnssec_LDADD= ext/polarssl-1.1.1/library/libpolarssl.a $(BOOST_PROGRAM_OPTIONS_LIBS) $(BOOST_SERIALIZATION_LIBS)
libbind2backend_la_SOURCES=bindbackend2.cc bindbackend2.hh bindparser.yy \
bindlexer.l ../../zoneparser-tng.cc ../../misc.cc \
-bindparser.hh ../../unix_utility.cc
+bindparser.hh ../../unix_utility.cc binddnssec.cc
libbind2backend_la_CXXFLAGS=$(AM_CXXFLAGS)
libbind2backend_la_CFLAGS=$(AM_CFLAGS)
string Bind2Backend::s_binddirectory;
/* when a query comes in, we find the most appropriate zone and answer from that */
+
BB2DomainInfo::BB2DomainInfo()
{
d_loaded=false;
for(vector<string>::const_iterator i=parts.begin()+1;i<parts.end();++i) {
if(state->name_id_map.count(*i)) {
BB2DomainInfo& bbd=state->id_zone_map[state->name_id_map[*i]];
-
- queueReload(&bbd);
+ Bind2Backend bb2;
+ bb2.queueReload(&bbd);
ret<< *i << ": "<< (bbd.d_loaded ? "": "[rejected]") <<"\t"<<bbd.d_status<<"\n";
}
else
return ret.str();
}
-Bind2Backend::Bind2Backend(const string &suffix)
+Bind2Backend::Bind2Backend(const string &suffix, bool loadZones)
{
#if __GNUC__ >= 3
std::ios_base::sync_with_stdio(false);
d_logprefix="[bind"+suffix+"backend]";
setArgPrefix("bind"+suffix);
Lock l(&s_startup_lock);
-
+
d_transaction_id=0;
+ setupDNSSEC();
if(!s_first) {
return;
}
- s_first=0;
+
s_state = shared_ptr<State>(new State);
- loadConfig();
-
+ if(loadZones) {
+ loadConfig();
+ s_first=0;
+ }
+
extern DynListener *dl;
dl->registerFunc("BIND-RELOAD-NOW", &DLReloadNowHandler);
dl->registerFunc("BIND-DOMAIN-STATUS", &DLDomStatusHandler);
}
sort(domains.begin(), domains.end()); // put stuff in inode order
- DNSSECKeeper dk;
for(vector<BindDomainInfo>::const_iterator i=domains.begin();
i!=domains.end();
++i)
L<<Logger::Info<<d_logprefix<<" parsing '"<<i->name<<"' from file '"<<i->filename<<"'"<<endl;
NSEC3PARAMRecordContent ns3pr;
- bool nsec3zone=dk.getNSEC3PARAM(i->name, &ns3pr);
+ bool nsec3zone=getNSEC3PARAM(i->name, &ns3pr);
try {
// we need to allocate a new vector so we don't kill the original, which is still in use!
ZoneParserTNG zpt(bbd->d_filename, bbd->d_name, s_binddirectory);
DNSResourceRecord rr;
string hashed;
- DNSSECKeeper dk;
NSEC3PARAMRecordContent ns3pr;
- bool nsec3zone=dk.getNSEC3PARAM(bbd->d_name, &ns3pr);
+ bool nsec3zone=getNSEC3PARAM(bbd->d_name, &ns3pr);
while(zpt.get(rr)) {
if(nsec3zone)
hashed=toLower(toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, rr.qname)));
{
shared_ptr<State> state = s_state;
BB2DomainInfo& bbd = state->id_zone_map[id];
- DNSSECKeeper dk;
NSEC3PARAMRecordContent ns3pr;
string auth=state->id_zone_map[id].d_name;
- if(!dk.getNSEC3PARAM(auth, &ns3pr)) {
+ if(!getNSEC3PARAM(auth, &ns3pr)) {
//cerr<<"in bind2backend::getBeforeAndAfterAbsolute: no nsec3 for "<<auth<<endl;
return findBeforeAndAfterUnhashed(bbd, qname, unhashed, before, after);
r.ttl=(d_iter)->ttl;
r.priority=(d_iter)->priority;
- if(!d_iter->auth && r.qtype.getCode() != QType::A && r.qtype.getCode()!=QType::AAAA && r.qtype.getCode() != QType::NS)
- cerr<<"Warning! Unauth response!"<<endl;
+ //if(!d_iter->auth && r.qtype.getCode() != QType::A && r.qtype.getCode()!=QType::AAAA && r.qtype.getCode() != QType::NS)
+ // cerr<<"Warning! Unauth response for qtype "<< r.qtype.getName() << " for '"<<r.qname<<"'"<<endl;
r.auth = d_iter->auth;
d_iter++;
declare(suffix,"supermaster-config","Location of (part of) named.conf where pdns can write zone-statements to","");
declare(suffix,"supermasters","List of IP-addresses of supermasters","");
declare(suffix,"supermaster-destdir","Destination directory for newly added slave zones",::arg()["config-dir"]);
+ declare(suffix,"dnssec-db","Filename to store & access our DNSSEC metadatabase, empty for none", "");
}
DNSBackend *make(const string &suffix="")
{
return new Bind2Backend(suffix);
}
+
+ DNSBackend *makeMetadataOnly(const string &suffix="")
+ {
+ return new Bind2Backend(suffix, false);
+ }
+
};
//! Magic class that is activated when the dynamic library is loaded
/*
PowerDNS Versatile Database Driven Nameserver
- Copyright (C) 2002-2010 PowerDNS.COM BV
+ Copyright (C) 2002-2012 PowerDNS.COM BV
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
#include <sys/stat.h>
#include <unistd.h>
#include "misc.hh"
+#include "dnsbackend.hh"
#include "namespaces.hh"
using namespace ::boost::multi_index;
time_t d_checkinterval;
};
+class SSQLite3;
+class NSEC3PARAMRecordContent;
+
class Bind2Backend : public DNSBackend
{
public:
- Bind2Backend(const string &suffix=""); //!< Makes our connection to the database. Calls exit(1) if it fails.
+ Bind2Backend(const string &suffix="", bool loadZones=true);
~Bind2Backend();
void getUnfreshSlaveInfos(vector<DomainInfo> *unfreshDomains);
void getUpdatedMasters(vector<DomainInfo> *changedDomains);
bool getDomainInfo(const string &domain, DomainInfo &di);
time_t getCtime(const string &fname);
+ // DNSSEC
virtual bool getBeforeAndAfterNamesAbsolute(uint32_t id, const std::string& qname, std::string& unhashed, std::string& before, std::string& after);
void lookup(const QType &, const string &qdomain, DNSPacket *p=0, int zoneId=-1);
bool list(const string &target, int id);
bool updateDNSSECOrderAndAuthAbsolute(uint32_t domain_id, const std::string& qname, const std::string& ordername, bool auth);
void alsoNotifies(const string &domain, set<string> *ips);
+// the DNSSEC related (getDomainMetadata has broader uses too)
+ virtual bool getDomainMetadata(const string& name, const std::string& kind, std::vector<std::string>& meta);
+ virtual bool setDomainMetadata(const string& name, const std::string& kind, const std::vector<std::string>& meta);
+ virtual bool getDomainKeys(const string& name, unsigned int kind, std::vector<KeyData>& keys);
+ virtual bool removeDomainKey(const string& name, unsigned int id);
+ virtual int addDomainKey(const string& name, const KeyData& key);
+ virtual bool activateDomainKey(const string& name, unsigned int id);
+ virtual bool deactivateDomainKey(const string& name, unsigned int id);
+ virtual bool getTSIGKey(const string& name, string* algorithm, string* content);
+ void createDNSSECDB(const string& fname="");
+ // end of DNSSEC
+
+
typedef map<string, int, CIStringCompare> name_id_map_t;
typedef map<uint32_t, BB2DomainInfo> id_zone_map_t;
bool createSlaveDomain(const string &ip, const string &domain, const string &account);
private:
+ void setupDNSSEC();
+ shared_ptr<SSQLite3> d_dnssecdb;
+ bool getNSEC3PARAM(const std::string& zname, NSEC3PARAMRecordContent* ns3p);
class handle
{
public:
ofstream *d_of;
handle d_handle;
- static void queueReload(BB2DomainInfo *bbd);
+ void queueReload(BB2DomainInfo *bbd);
bool findBeforeAndAfterUnhashed(BB2DomainInfo& bbd, const std::string& qname, std::string& unhashed, std::string& before, std::string& after);
void reload();
static string DLDomStatusHandler(const vector<string>&parts, Utility::pid_t ppid);
--- /dev/null
+/*
+ PowerDNS Versatile Database Driven Nameserver
+ Copyright (C) 2002-2012 PowerDNS.COM BV
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2
+ as published by the Free Software Foundation
+
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+#include "bindbackend2.hh"
+#include "../../../modules/gsqlite3backend/ssqlite3.hh"
+#include "dnsrecords.hh"
+#include "bind-dnssec.schema.sqlite3.sql.h"
+
+void Bind2Backend::setupDNSSEC()
+{
+ // cerr<<"Settting up dnssec db.. "<<getArg("dnssec-db") <<endl;
+ if(getArg("dnssec-db").empty())
+ return;
+ try {
+ d_dnssecdb = shared_ptr<SSQLite3>(new SSQLite3(getArg("dnssec-db")));
+ }
+ catch(SSqlException& se) {
+ // this error is meant to kill the server dead - it makes no sense to continue..
+ throw runtime_error("Error opening DNSSEC database in BIND backend: "+se.txtReason());
+ }
+}
+
+void Bind2Backend::createDNSSECDB(const string& fname)
+{
+ string dbname = fname.empty() ? getArg("dnssec-db") : fname;
+ if(dbname.empty())
+ throw AhuException("Unable to generate DNSSEC database for BIND backend since no name was configured for it");
+
+ try {
+ SSQLite3 db(dbname, true); // create=ok
+ db.doCommand(sqlCreate);
+ }
+ catch(SSqlException& se) {
+ throw AhuException("Error creating database in BIND backend: "+se.txtReason());
+ }
+}
+
+
+bool Bind2Backend::getNSEC3PARAM(const std::string& zname, NSEC3PARAMRecordContent* ns3p)
+{
+ string value;
+
+ vector<string> meta;
+ getDomainMetadata(zname, "NSEC3PARAM", meta);
+ if(!meta.empty())
+ value=*meta.begin();
+
+ if(value.empty()) { // "no NSEC3"
+ return false;
+ }
+
+ if(ns3p) {
+ NSEC3PARAMRecordContent* tmp=dynamic_cast<NSEC3PARAMRecordContent*>(DNSRecordContent::mastermake(QType::NSEC3PARAM, 1, value));
+ *ns3p = *tmp;
+ delete tmp;
+ }
+ return true;
+}
+
+bool Bind2Backend::getDomainMetadata(const string& name, const std::string& kind, std::vector<std::string>& meta)
+{
+ if(!d_dnssecdb)
+ return false;
+
+ // cerr<<"Asked to get metadata for zone '"<<name<<"'|"<<kind<<"\n";
+
+ boost::format fmt("select content from domainmetadata where domain='%s' and kind='%s'");
+ try {
+ d_dnssecdb->doQuery((fmt % d_dnssecdb->escape(name) % d_dnssecdb->escape(kind)).str());
+
+ vector<string> row;
+ while(d_dnssecdb->getRow(row)) {
+ meta.push_back(row[0]);
+ }
+ }
+ catch(SSqlException& se) {
+ throw AhuException("Error accessing DNSSEC database in BIND backend: "+se.txtReason());
+ }
+ return true;
+}
+
+bool Bind2Backend::setDomainMetadata(const string& name, const std::string& kind, const std::vector<std::string>& meta)
+{
+ if(!d_dnssecdb)
+ return false;
+
+ boost::format fmt("delete from domainmetadata where domain='%s' and kind='%s'");
+ boost::format fmt2("insert into domainmetadata (domain, kind, content) values ('%s','%s', '%s')");
+ try {
+ d_dnssecdb->doCommand((fmt % d_dnssecdb->escape(name) % d_dnssecdb->escape(kind)).str());
+ if(!meta.empty())
+ d_dnssecdb->doCommand((fmt2 % d_dnssecdb->escape(name) % d_dnssecdb->escape(kind) % d_dnssecdb->escape(meta.begin()->c_str())).str());
+ }
+ catch(SSqlException& se) {
+ throw AhuException("Error accessing DNSSEC database in BIND backend: "+se.txtReason());
+ }
+ return true;
+
+}
+
+bool Bind2Backend::getDomainKeys(const string& name, unsigned int kind, std::vector<KeyData>& keys)
+{
+ // cerr<<"Asked to get keys for zone '"<<name<<"'\n";
+ if(!d_dnssecdb)
+ return false;
+ boost::format fmt("select id,flags, active, content from cryptokeys where domain='%s'");
+ try {
+ d_dnssecdb->doQuery((fmt % d_dnssecdb->escape(name)).str());
+ KeyData kd;
+ vector<string> row;
+ while(d_dnssecdb->getRow(row)) {
+ kd.id = atoi(row[0].c_str());
+ kd.flags = atoi(row[1].c_str());
+ kd.active = atoi(row[2].c_str());
+ kd.content = row[3];
+ keys.push_back(kd);
+ }
+ }
+ catch(SSqlException& se) {
+ throw AhuException("Error accessing DNSSEC database in BIND backend: "+se.txtReason());
+ }
+
+ return true;
+}
+
+bool Bind2Backend::removeDomainKey(const string& name, unsigned int id)
+{
+ if(!d_dnssecdb)
+ return false;
+
+ cerr<<"Asked to remove key "<<id<<" in zone '"<<name<<"'\n";
+
+ boost::format fmt("delete from cryptokeys where domain='%s' and id=%d");
+ try {
+ d_dnssecdb->doCommand((fmt % d_dnssecdb->escape(name) % id).str());
+ }
+ catch(SSqlException& se) {
+ cerr<<se.txtReason() <<endl;
+ }
+
+ return true;
+}
+
+int Bind2Backend::addDomainKey(const string& name, const KeyData& key)
+{
+ if(!d_dnssecdb)
+ return false;
+
+ //cerr<<"Asked to add a key to zone '"<<name<<"'\n";
+
+ boost::format fmt("insert into cryptokeys (domain, flags, active, content) values ('%s', %d, %d, '%s')");
+ try {
+ d_dnssecdb->doCommand((fmt % d_dnssecdb->escape(name) % key.flags % key.active % d_dnssecdb->escape(key.content)).str());
+ }
+ catch(SSqlException& se) {
+ throw AhuException("Error accessing DNSSEC database in BIND backend: "+se.txtReason());
+ }
+
+ return true;
+}
+
+bool Bind2Backend::activateDomainKey(const string& name, unsigned int id)
+{
+ // cerr<<"Asked to activate key "<<id<<" inzone '"<<name<<"'\n";
+ if(!d_dnssecdb)
+ return false;
+
+ boost::format fmt("update cryptokeys set active=1 where domain='%s' and id=%d");
+ try {
+ d_dnssecdb->doCommand((fmt % d_dnssecdb->escape(name) % id).str());
+ }
+ catch(SSqlException& se) {
+ throw AhuException("Error accessing DNSSEC database in BIND backend: "+se.txtReason());
+ }
+
+ return true;
+}
+
+bool Bind2Backend::deactivateDomainKey(const string& name, unsigned int id)
+{
+ // cerr<<"Asked to deactivate key "<<id<<" inzone '"<<name<<"'\n";
+ if(!d_dnssecdb)
+ return false;
+
+ boost::format fmt("update cryptokeys set active=0 where domain='%s' and id=%d");
+ try {
+ d_dnssecdb->doCommand((fmt % d_dnssecdb->escape(name) % id).str());
+ }
+ catch(SSqlException& se) {
+ throw AhuException("Error accessing DNSSEC database in BIND backend: "+se.txtReason());
+ }
+
+ return true;
+}
+
+bool Bind2Backend::getTSIGKey(const string& name, string* algorithm, string* content)
+{
+ if(!d_dnssecdb)
+ return false;
+ boost::format fmt("select algorithm, secret from tsigkeys where name='%s'");
+
+ try {
+ d_dnssecdb->doQuery( (fmt % d_dnssecdb->escape(name)).str());
+ }
+ catch (SSqlException &e) {
+ throw AhuException("GSQLBackend unable to retrieve named TSIG key: "+e.txtReason());
+ }
+
+ SSql::row_t row;
+
+ content->clear();
+ while(d_dnssecdb->getRow(row)) {
+ *algorithm = row[0];
+ *content=row[1];
+ }
+
+ return !content->empty();
+
+}
create table domainmetadata (
- id INTEGER PRIMARY KEY,
+ id INTEGER PRIMARY KEY,
domain VARCHAR(255) COLLATE NOCASE,
- kind VARCHAR(16) COLLATE NOCASE,
+ kind VARCHAR(16) COLLATE NOCASE,
content TEXT
);
getFromMeta(zname, "NSEC3NARROW", value);
*narrow = (value=="1");
}
-
-
return true;
}
return d_instances.size();
}
-vector<DNSBackend *>BackendMakerClass::all(bool skipBIND)
+vector<DNSBackend *>BackendMakerClass::all(bool metadataOnly)
{
vector<DNSBackend *>ret;
if(d_instances.empty())
try {
for(vector<pair<string,string> >::const_iterator i=d_instances.begin();i!=d_instances.end();++i) {
- if(skipBIND && i->first=="bind")
- continue;
- DNSBackend *made=d_repository[i->first]->make(i->second);
+ DNSBackend *made;
+ if(metadataOnly)
+ made = d_repository[i->first]->makeMetadataOnly(i->second);
+ else
+ made = d_repository[i->first]->make(i->second);
if(!made)
throw AhuException("Unable to launch backend '"+i->first+"'");
BackendFactory(const string &name) : d_name(name) {}
virtual ~BackendFactory(){}
virtual DNSBackend *make(const string &suffix)=0;
+ virtual DNSBackend *makeMetadataOnly(const string &suffix)
+ {
+ return this->make(suffix);
+ }
virtual void declareArguments(const string &suffix=""){}
const string &getName() const;
#include "zoneparser-tng.hh"
#include "signingpipe.hh"
#include <boost/scoped_ptr.hpp>
+#include "bindbackend2.hh"
StatBag S;
PacketCache PC;
cerr<<" [rsasha1|rsasha256|rsasha512|gost|ecdsa256|ecdsa384]\n";
cerr<<" Add a ZSK or KSK to zone and specify algo&bits\n";
cerr<<"check-zone ZONE Check a zone for correctness\n";
+ cerr<<"create-bind-db [FNAME] Create a DNSSEC database for BIND backend\n";
cerr<<"deactivate-zone-key ZONE KEY-ID Deactivate the key with key id KEY-ID in ZONE\n";
cerr<<"disable-dnssec ZONE Deactivate all keys and unset PRESIGNED in ZONE\n";
cerr<<"export-zone-dnskey ZONE KEY-ID Export to stdout the public DNSKEY described\n";
reportAllTypes();
DNSSECKeeper dk;
- if(cmds[0] == "rectify-zone") {
+ if(cmds[0] == "create-bind-db") {
+ Bind2Backend b2b;
+ b2b.createDNSSECDB(cmds.size() > 1 ? cmds[1] : "");
+ }
+ else if(cmds[0] == "rectify-zone") {
if(cmds.size() < 2) {
cerr << "Syntax: pdnssec rectify-zone ZONE [ZONE..]"<<endl;
return 0;