From 2717b8b383de410934faeb497642c9ea41e6ffc9 Mon Sep 17 00:00:00 2001 From: Bert Hubert Date: Sun, 4 Mar 2012 11:25:26 +0000 Subject: [PATCH] this commit implements a DNSSEC metadata store for the BIND backend. To make this possible, it has been necessary to define a 'metadata only' mode for backends, because otherwise every invocation of 'pdnssec' would load all BIND zones (of which there could be millions). In turn to make this possible, we have had to teach the BIND backend not to rely on the DNSSECKeeper anymore, since that would setup a circular dependency. This means that the BIND backend, when it needs to ask a DNSSEC metadata-related question, only asks itself. In other words, it will no longer get DNSSEC related metadata from other backends. To benefit from the built in DNSSEC store, set 'bind-dnssec-db=/etc/bind/dnssec.sqlite3', issue pdnssec create-bind-db, and you are in business! git-svn-id: svn://svn.powerdns.com/pdns/trunk/pdns@2450 d19b8d6e-7fed-0310-83ef-9ca221ded41b --- pdns/Makefile.am | 12 +- pdns/backends/bind/Makefile.am | 2 +- pdns/backends/bind/bindbackend2.cc | 39 +++-- pdns/backends/bind/bindbackend2.hh | 27 +++- pdns/backends/bind/binddnssec.cc | 234 ++++++++++++++++++++++++++++ pdns/bind-dnssec.schema.sqlite3.sql | 4 +- pdns/dbdnsseckeeper.cc | 2 - pdns/dnsbackend.cc | 10 +- pdns/dnsbackend.hh | 4 + pdns/pdnssec.cc | 8 +- 10 files changed, 310 insertions(+), 32 deletions(-) create mode 100644 pdns/backends/bind/binddnssec.cc diff --git a/pdns/Makefile.am b/pdns/Makefile.am index 672ffe687..890bff7c1 100644 --- a/pdns/Makefile.am +++ b/pdns/Makefile.am @@ -1,7 +1,8 @@ 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 @@ -30,7 +31,7 @@ dynlistener.cc dynlistener.hh dynhandler.cc dynhandler.hh \ 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 \ @@ -69,12 +70,15 @@ pdnssec_SOURCES=pdnssec.cc dbdnsseckeeper.cc sstuff.hh dnsparser.cc dnsparser.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) diff --git a/pdns/backends/bind/Makefile.am b/pdns/backends/bind/Makefile.am index ff667f257..4eff43c29 100644 --- a/pdns/backends/bind/Makefile.am +++ b/pdns/backends/bind/Makefile.am @@ -4,7 +4,7 @@ AM_CPPFLAGS=$(BOOST_CPPFLAGS) @THREADFLAGS@ 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) diff --git a/pdns/backends/bind/bindbackend2.cc b/pdns/backends/bind/bindbackend2.cc index 69cc253c0..aaf5eb4df 100644 --- a/pdns/backends/bind/bindbackend2.cc +++ b/pdns/backends/bind/bindbackend2.cc @@ -83,6 +83,7 @@ pthread_mutex_t Bind2Backend::s_state_swap_lock=PTHREAD_MUTEX_INITIALIZER; string Bind2Backend::s_binddirectory; /* when a query comes in, we find the most appropriate zone and answer from that */ + BB2DomainInfo::BB2DomainInfo() { d_loaded=false; @@ -449,8 +450,8 @@ string Bind2Backend::DLReloadNowHandler(const vector&parts, Utility::pid for(vector::const_iterator i=parts.begin()+1;iname_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"<&parts, Utility::p 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); @@ -508,15 +509,19 @@ Bind2Backend::Bind2Backend(const string &suffix) 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(new State); - loadConfig(); - + if(loadZones) { + loadConfig(); + s_first=0; + } + extern DynListener *dl; dl->registerFunc("BIND-RELOAD-NOW", &DLReloadNowHandler); dl->registerFunc("BIND-DOMAIN-STATUS", &DLDomStatusHandler); @@ -618,7 +623,6 @@ void Bind2Backend::loadConfig(string* status) } sort(domains.begin(), domains.end()); // put stuff in inode order - DNSSECKeeper dk; for(vector::const_iterator i=domains.begin(); i!=domains.end(); ++i) @@ -660,7 +664,7 @@ void Bind2Backend::loadConfig(string* status) L<name<<"' from file '"<filename<<"'"<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! @@ -790,9 +794,8 @@ void Bind2Backend::queueReload(BB2DomainInfo *bbd) 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))); @@ -862,11 +865,10 @@ bool Bind2Backend::getBeforeAndAfterNamesAbsolute(uint32_t id, const std::string { shared_ptr 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 "<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!"<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 '"<auth; d_iter++; @@ -1221,12 +1223,19 @@ class Bind2Factory : public BackendFactory 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 diff --git a/pdns/backends/bind/bindbackend2.hh b/pdns/backends/bind/bindbackend2.hh index dde2cfc4d..de1b77ce9 100644 --- a/pdns/backends/bind/bindbackend2.hh +++ b/pdns/backends/bind/bindbackend2.hh @@ -1,6 +1,6 @@ /* 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 @@ -33,6 +33,7 @@ #include #include #include "misc.hh" +#include "dnsbackend.hh" #include "namespaces.hh" using namespace ::boost::multi_index; @@ -118,15 +119,19 @@ private: 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 *unfreshDomains); void getUpdatedMasters(vector *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); @@ -145,6 +150,19 @@ public: bool updateDNSSECOrderAndAuthAbsolute(uint32_t domain_id, const std::string& qname, const std::string& ordername, bool auth); void alsoNotifies(const string &domain, set *ips); +// the DNSSEC related (getDomainMetadata has broader uses too) + virtual bool getDomainMetadata(const string& name, const std::string& kind, std::vector& meta); + virtual bool setDomainMetadata(const string& name, const std::string& kind, const std::vector& meta); + virtual bool getDomainKeys(const string& name, unsigned int kind, std::vector& 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 name_id_map_t; typedef map id_zone_map_t; @@ -164,6 +182,9 @@ public: bool createSlaveDomain(const string &ip, const string &domain, const string &account); private: + void setupDNSSEC(); + shared_ptr d_dnssecdb; + bool getNSEC3PARAM(const std::string& zname, NSEC3PARAMRecordContent* ns3p); class handle { public: @@ -217,7 +238,7 @@ private: 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&parts, Utility::pid_t ppid); diff --git a/pdns/backends/bind/binddnssec.cc b/pdns/backends/bind/binddnssec.cc new file mode 100644 index 000000000..791e0d4aa --- /dev/null +++ b/pdns/backends/bind/binddnssec.cc @@ -0,0 +1,234 @@ +/* + 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.. "<(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 meta; + getDomainMetadata(zname, "NSEC3PARAM", meta); + if(!meta.empty()) + value=*meta.begin(); + + if(value.empty()) { // "no NSEC3" + return false; + } + + if(ns3p) { + NSEC3PARAMRecordContent* tmp=dynamic_cast(DNSRecordContent::mastermake(QType::NSEC3PARAM, 1, value)); + *ns3p = *tmp; + delete tmp; + } + return true; +} + +bool Bind2Backend::getDomainMetadata(const string& name, const std::string& kind, std::vector& meta) +{ + if(!d_dnssecdb) + return false; + + // cerr<<"Asked to get metadata for zone '"<doQuery((fmt % d_dnssecdb->escape(name) % d_dnssecdb->escape(kind)).str()); + + vector 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& 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& keys) +{ + // cerr<<"Asked to get keys for zone '"<doQuery((fmt % d_dnssecdb->escape(name)).str()); + KeyData kd; + vector 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 "<doCommand((fmt % d_dnssecdb->escape(name) % id).str()); + } + catch(SSqlException& se) { + cerr<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 "<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 "<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(); + +} diff --git a/pdns/bind-dnssec.schema.sqlite3.sql b/pdns/bind-dnssec.schema.sqlite3.sql index 1fdff1d95..34a7645cd 100644 --- a/pdns/bind-dnssec.schema.sqlite3.sql +++ b/pdns/bind-dnssec.schema.sqlite3.sql @@ -1,7 +1,7 @@ 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 ); diff --git a/pdns/dbdnsseckeeper.cc b/pdns/dbdnsseckeeper.cc index 60a6f2bce..39c7fddf2 100644 --- a/pdns/dbdnsseckeeper.cc +++ b/pdns/dbdnsseckeeper.cc @@ -232,8 +232,6 @@ bool DNSSECKeeper::getNSEC3PARAM(const std::string& zname, NSEC3PARAMRecordConte getFromMeta(zname, "NSEC3NARROW", value); *narrow = (value=="1"); } - - return true; } diff --git a/pdns/dnsbackend.cc b/pdns/dnsbackend.cc index 89371b54d..acff3dff0 100644 --- a/pdns/dnsbackend.cc +++ b/pdns/dnsbackend.cc @@ -163,7 +163,7 @@ int BackendMakerClass::numLauncheable() return d_instances.size(); } -vectorBackendMakerClass::all(bool skipBIND) +vectorBackendMakerClass::all(bool metadataOnly) { vectorret; if(d_instances.empty()) @@ -171,9 +171,11 @@ vectorBackendMakerClass::all(bool skipBIND) try { for(vector >::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+"'"); diff --git a/pdns/dnsbackend.hh b/pdns/dnsbackend.hh index 6f904b0d4..718e12afa 100644 --- a/pdns/dnsbackend.hh +++ b/pdns/dnsbackend.hh @@ -235,6 +235,10 @@ public: 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; diff --git a/pdns/pdnssec.cc b/pdns/pdnssec.cc index 8162a5153..62e0b5259 100644 --- a/pdns/pdnssec.cc +++ b/pdns/pdnssec.cc @@ -12,6 +12,7 @@ #include "zoneparser-tng.hh" #include "signingpipe.hh" #include +#include "bindbackend2.hh" StatBag S; PacketCache PC; @@ -433,6 +434,7 @@ try 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"; @@ -462,7 +464,11 @@ try 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..]"<