+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
#include "dnsseckeeper.hh"
#include "dnssecinfra.hh"
#include "statbag.hh"
::arg().set("default-ksk-algorithms","Default KSK algorithms")="rsasha256";
::arg().set("default-ksk-size","Default KSK size (0 means default)")="0";
::arg().set("default-zsk-algorithms","Default ZSK algorithms")="rsasha256";
- ::arg().set("default-zsk-size","Default KSK size (0 means default)")="0";
+ ::arg().set("default-zsk-size","Default ZSK size (0 means default)")="0";
::arg().set("max-ent-entries", "Maximum number of empty non-terminals in a zone")="100000";
::arg().set("module-dir","Default directory for modules")=PKGLIBDIR;
::arg().set("entropy-source", "If set, read entropy from this file")="/dev/urandom";
-
+ ::arg().setSwitch("query-logging","Hint backends that queries should be logged")="no";
+ ::arg().set("loglevel","Amount of logging. Higher is more.")="0";
::arg().setSwitch("direct-dnskey","Fetch DNSKEY RRs from backend during DNSKEY synthesis")="no";
::arg().set("max-nsec3-iterations","Limit the number of NSEC3 hash iterations")="500"; // RFC5155 10.3
::arg().set("max-signature-cache-entries", "Maximum number of signatures cache entries")="";
::arg().laxFile(configname.c_str());
+ L.toConsole(Logger::Error); // so we print any errors
BackendMakers().launch(::arg()["launch"]); // vrooooom!
+ L.toConsole((Logger::Urgency)(::arg().asNum("loglevel")));
::arg().laxFile(configname.c_str());
//cerr<<"Backend: "<<::arg()["launch"]<<", '" << ::arg()["gmysql-dbname"] <<"'" <<endl;
::arg().set("soa-refresh-default","Default SOA refresh")="10800";
::arg().set("soa-retry-default","Default SOA retry")="3600";
::arg().set("soa-expire-default","Default SOA expire")="604800";
- ::arg().setSwitch("query-logging","Hint backends that queries should be logged")="no";
::arg().set("soa-minimum-ttl","Default SOA minimum ttl")="3600";
UeberBackend::go();
// irritatingly enough, rectifyZone needs its own ueberbackend and can't therefore benefit from transactions outside its scope
// I think this has to do with interlocking transactions between B and DK, but unsure.
-bool rectifyZone(DNSSECKeeper& dk, const std::string& zone)
+bool rectifyZone(DNSSECKeeper& dk, const DNSName& zone)
{
if(dk.isPresigned(zone)){
- cerr<<"Rectify presigned zone '"<<zone<<"' is not allowed/necessary."<<endl;
+ cerr<<"Rectify presigned zone '"<<zone.toString()<<"' is not allowed/necessary."<<endl;
return false;
}
UeberBackend B("default");
bool doTransaction=true; // but see above
SOAData sd;
- sd.db = (DNSBackend*)-1;
- if(!B.getSOA(zone, sd)) {
- cerr<<"No SOA known for '"<<zone<<"', is such a zone in the database?"<<endl;
+ if(!B.getSOAUncached(zone, sd)) {
+ cerr<<"No SOA known for '"<<zone.toString()<<"', is such a zone in the database?"<<endl;
return false;
}
sd.db->list(zone, sd.domain_id);
DNSResourceRecord rr;
- set<string> qnames, nsset, dsnames, insnonterm, delnonterm;
- map<string,bool> nonterm;
+ set<DNSName> qnames, nsset, dsnames, insnonterm, delnonterm;
+ map<DNSName,bool> nonterm;
bool doent=true;
while(sd.db->get(rr)) {
if (rr.qtype.getCode())
{
qnames.insert(rr.qname);
- if(rr.qtype.getCode() == QType::NS && !pdns_iequals(rr.qname, zone))
+ if(rr.qtype.getCode() == QType::NS && rr.qname!=zone)
nsset.insert(rr.qname);
if(rr.qtype.getCode() == QType::DS)
dsnames.insert(rr.qname);
cerr<<"Adding NSEC ordering information "<<endl;
else if(!narrow) {
if(!isOptOut)
- cerr<<"Adding NSEC3 hashed ordering information for '"<<zone<<"'"<<endl;
+ cerr<<"Adding NSEC3 hashed ordering information for '"<<zone.toString()<<"'"<<endl;
else
- cerr<<"Adding NSEC3 opt-out hashed ordering information for '"<<zone<<"'"<<endl;
+ cerr<<"Adding NSEC3 opt-out hashed ordering information for '"<<zone.toString()<<"'"<<endl;
} else
cerr<<"Erasing NSEC3 ordering since we are narrow, only setting 'auth' fields"<<endl;
}
uint32_t maxent = ::arg().asNum("max-ent-entries");
dononterm:;
- BOOST_FOREACH(const string& qname, qnames)
+ BOOST_FOREACH(const DNSName& qname, qnames)
{
bool auth=true;
- string shorter(qname);
+ DNSName shorter(qname);
if(realrr) {
do {
auth=false;
break;
}
- } while(chopOff(shorter));
+ } while(shorter.chopOff());
}
if(haveNSEC3)
if(!narrow && (realrr || !isOptOut || nonterm.find(qname)->second)) {
hashed=toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, qname));
if(g_verbose)
- cerr<<"'"<<qname<<"' -> '"<< hashed <<"'"<<endl;
+ cerr<<"'"<<qname.toString()<<"' -> '"<< hashed <<"'"<<endl;
sd.db->updateDNSSECOrderAndAuthAbsolute(sd.domain_id, qname, hashed, auth);
}
else {
if(doent)
{
shorter=qname;
- while(!pdns_iequals(shorter, zone) && chopOff(shorter))
+ while(shorter!=zone && shorter.chopOff())
{
if(!qnames.count(shorter))
{
if(!(maxent))
{
- cerr<<"Zone '"<<zone<<"' has too many empty non terminals."<<endl;
+ cerr<<"Zone '"<<zone.toString()<<"' has too many empty non terminals."<<endl;
insnonterm.clear();
delnonterm.clear();
doent=false;
delnonterm.erase(shorter);
if (!nonterm.count(shorter)) {
- nonterm.insert(pair<string, bool>(shorter, auth));
+ nonterm.insert(pair<DNSName, bool>(shorter, auth));
--maxent;
} else if (auth)
nonterm[shorter]=true;
{
realrr=false;
qnames.clear();
- pair<string,bool> nt;
+ pair<DNSName,bool> nt;
BOOST_FOREACH(nt, nonterm){
qnames.insert(nt.first);
}
B.getAllDomains(&domainInfo);
BOOST_FOREACH(DomainInfo di, domainInfo) {
- cerr<<"Rectifying "<<di.zone<<": ";
+ cerr<<"Rectifying "<<di.zone.toString()<<": ";
rectifyZone(dk, di.zone);
}
cout<<"Rectified "<<domainInfo.size()<<" zones."<<endl;
}
-int checkZone(DNSSECKeeper &dk, UeberBackend &B, const std::string& zone)
+int checkZone(DNSSECKeeper &dk, UeberBackend &B, const DNSName& zone)
{
SOAData sd;
- sd.db=(DNSBackend*)-1;
- if(!B.getSOA(zone, sd)) {
- cout<<"[error] No SOA record present, or active, in zone '"<<zone<<"'"<<endl;
- cout<<"Checked 0 records of '"<<zone<<"', 1 errors, 0 warnings."<<endl;
+ if(!B.getSOAUncached(zone, sd)) {
+ cout<<"[error] No SOA record present, or active, in zone '"<<zone.toString()<<"'"<<endl;
+ cout<<"Checked 0 records of '"<<zone.toString()<<"', 1 errors, 0 warnings."<<endl;
return 1;
}
+
+ NSEC3PARAMRecordContent ns3pr;
+ bool narrow = false;
+ bool haveNSEC3 = dk.getNSEC3PARAM(zone, &ns3pr, &narrow);
+ bool isOptOut=(haveNSEC3 && ns3pr.d_flags);
+
+ bool isSecure=dk.isSecuredZone(zone);
bool presigned=dk.isPresigned(zone);
- sd.db->list(zone, sd.domain_id, true);
+
DNSResourceRecord rr;
uint64_t numrecords=0, numerrors=0, numwarnings=0;
+
+ // Check for delegation in parent zone
+ string parent(zone);
+ while(chopOff(parent)) {
+ SOAData sd_p;
+ if(B.getSOAUncached(parent, sd_p)) {
+ bool ns=false;
+ DNSResourceRecord rr;
+ B.lookup(QType(QType::ANY), zone, NULL, sd_p.domain_id);
+ while(B.get(rr))
+ ns |= (rr.qtype == QType::NS);
+ if (!ns) {
+ cerr<<"[Error] No delegation for zone '"<<zone<<"' in parent '"<<parent<<"'"<<endl;
+ numerrors++;
+ }
+ break;
+ }
+ }
+
+
bool hasNsAtApex = false;
- set<string> records, cnames, noncnames;
+ set<DNSName> cnames, noncnames, glue, checkglue;
+ set<string> records;
map<string, unsigned int> ttl;
ostringstream content;
pair<map<string, unsigned int>::iterator,bool> ret;
+ sd.db->list(zone, sd.domain_id, true);
+
while(sd.db->get(rr)) {
if(!rr.qtype.getCode())
continue;
tmp = drc->getZoneRepresentation();
if (rr.qtype.getCode() != QType::AAAA) {
if (!pdns_iequals(tmp, rr.content)) {
- cout<<"[Warning] Parsed and original record content are not equal: "<<rr.qname<<" IN " <<rr.qtype.getName()<< " '" << rr.content<<"' (Content parsed as '"<<tmp<<"')"<<endl;
+ cout<<"[Warning] Parsed and original record content are not equal: "<<rr.qname.toString()<<" IN " <<rr.qtype.getName()<< " '" << rr.content<<"' (Content parsed as '"<<tmp<<"')"<<endl;
numwarnings++;
}
} else {
struct in6_addr tmpbuf;
if (inet_pton(AF_INET6, rr.content.c_str(), &tmpbuf) != 1 || rr.content.find('.') != string::npos) {
- cout<<"[Warning] Following record is not a valid IPv6 address: "<<rr.qname<<" IN " <<rr.qtype.getName()<< " '" << rr.content<<"'"<<endl;
+ cout<<"[Warning] Following record is not a valid IPv6 address: "<<rr.qname.toString()<<" IN " <<rr.qtype.getName()<< " '" << rr.content<<"'"<<endl;
numwarnings++;
}
}
}
catch(std::exception& e)
{
- cout<<"[Error] Following record had a problem: "<<rr.qname<<" IN " <<rr.qtype.getName()<< " " << rr.content<<endl;
+ cout<<"[Error] Following record had a problem: "<<rr.qname.toString()<<" IN " <<rr.qtype.getName()<< " " << rr.content<<endl;
cout<<"[Error] Error was: "<<e.what()<<endl;
numerrors++;
continue;
}
- if(!endsOn(rr.qname, zone)) {
- cout<<"[Warning] Record '"<<rr.qname<<" IN "<<rr.qtype.getName()<<" "<<rr.content<<"' in zone '"<<zone<<"' is out-of-zone."<<endl;
+ if(!rr.qname.isPartOf(zone)) {
+ cout<<"[Warning] Record '"<<rr.qname.toString()<<" IN "<<rr.qtype.getName()<<" "<<rr.content<<"' in zone '"<<zone.toString()<<"' is out-of-zone."<<endl;
numwarnings++;
continue;
}
content.str("");
- content<<rr.qname<<" "<<rr.qtype.getName()<<" "<<rr.content;
+ content<<rr.qname.toString()<<" "<<rr.qtype.getName()<<" "<<rr.content;
if (records.count(toLower(content.str()))) {
- cout<<"[Error] Duplicate record found in rrset: '"<<rr.qname<<" IN "<<rr.qtype.getName()<<" "<<rr.content<<"'"<<endl;
+ cout<<"[Error] Duplicate record found in rrset: '"<<rr.qname.toString()<<" IN "<<rr.qtype.getName()<<" "<<rr.content<<"'"<<endl;
numerrors++;
continue;
} else
records.insert(toLower(content.str()));
content.str("");
- content<<rr.qname<<" "<<rr.qtype.getName();
+ content<<rr.qname.toString()<<" "<<rr.qtype.getName();
+ if (rr.qtype.getCode() == QType::RRSIG) {
+ RRSIGRecordContent rrc(rr.content);
+ content<<" ("<<DNSRecordContent::NumberToType(rrc.d_type)<<")";
+ }
ret = ttl.insert(pair<string, unsigned int>(toLower(content.str()), rr.ttl));
if (ret.second == false && ret.first->second != rr.ttl) {
- cout<<"[Error] TTL mismatch in rrset: '"<<rr.qname<<" IN " <<rr.qtype.getName()<<" "<<rr.content<<"' ("<<ret.first->second<<" != "<<rr.ttl<<")"<<endl;
+ cout<<"[Error] TTL mismatch in rrset: '"<<rr.qname.toString()<<" IN " <<rr.qtype.getName()<<" "<<rr.content<<"' ("<<ret.first->second<<" != "<<rr.ttl<<")"<<endl;
numerrors++;
continue;
}
- if(pdns_iequals(rr.qname, zone)) {
+ if (isSecure && isOptOut && (rr.qname.countLabels() && rr.qname.getRawLabels()[0] == "*")) {
+ cout<<"[Warning] wildcard record '"<<rr.qname.toString()<<" IN " <<rr.qtype.getName()<<" "<<rr.content<<"' is insecure"<<endl;
+ cout<<"[Info] Wildcard records in opt-out zones are insecure. Disable the opt-out flag for this zone to avoid this warning. Command: pdnssec set-nsec3 "<<zone.toString()<<endl;
+ numwarnings++;
+ }
+
+ if(rr.qname==zone) {
if (rr.qtype.getCode() == QType::NS) {
hasNsAtApex=true;
} else if (rr.qtype.getCode() == QType::DS) {
- cout<<"[Warning] DS at apex in zone '"<<zone<<"', should no be here."<<endl;
+ cout<<"[Warning] DS at apex in zone '"<<zone.toString()<<"', should not be here."<<endl;
numwarnings++;
}
} else {
if (rr.qtype.getCode() == QType::SOA) {
- cout<<"[Error] SOA record not at apex '"<<rr.qname<<" IN "<<rr.qtype.getName()<<" "<<rr.content<<"' in zone '"<<zone<<"'"<<endl;
+ cout<<"[Error] SOA record not at apex '"<<rr.qname.toString()<<" IN "<<rr.qtype.getName()<<" "<<rr.content<<"' in zone '"<<zone.toString()<<"'"<<endl;
numerrors++;
continue;
} else if (rr.qtype.getCode() == QType::DNSKEY) {
- cout<<"[Warning] DNSKEY record not at apex '"<<rr.qname<<" IN "<<rr.qtype.getName()<<" "<<rr.content<<"' in zone '"<<zone<<"', should not be here."<<endl;
+ cout<<"[Warning] DNSKEY record not at apex '"<<rr.qname.toString()<<" IN "<<rr.qtype.getName()<<" "<<rr.content<<"' in zone '"<<zone.toString()<<"', should not be here."<<endl;
numwarnings++;
+ } else if (rr.qtype.getCode() == QType::NS && endsOn(rr.content, rr.qname)) {
+ checkglue.insert(toLower(rr.content));
+ } else if (rr.qtype.getCode() == QType::A || rr.qtype.getCode() == QType::AAAA) {
+ glue.insert(toLower(rr.qname));
}
}
if (rr.qtype.getCode() == QType::CNAME) {
- if (!cnames.count(toLower(rr.qname)))
- cnames.insert(toLower(rr.qname));
+ if (!cnames.count(rr.qname))
+ cnames.insert(rr.qname);
else {
- cout<<"[Error] Duplicate CNAME found at '"<<rr.qname<<"'"<<endl;
+ cout<<"[Error] Duplicate CNAME found at '"<<rr.qname.toString()<<"'"<<endl;
numerrors++;
continue;
}
} else {
if (rr.qtype.getCode() == QType::RRSIG) {
if(!presigned) {
- cout<<"[Error] RRSIG found at '"<<rr.qname<<"' in non-presigned zone. These do not belong in the database."<<endl;
+ cout<<"[Error] RRSIG found at '"<<rr.qname.toString()<<"' in non-presigned zone. These do not belong in the database."<<endl;
numerrors++;
continue;
}
} else
- noncnames.insert(toLower(rr.qname));
+ noncnames.insert(rr.qname);
}
if(rr.qtype.getCode() == QType::NSEC || rr.qtype.getCode() == QType::NSEC3)
{
- cout<<"[Error] NSEC or NSEC3 found at '"<<rr.qname<<"'. These do not belong in the database."<<endl;
+ cout<<"[Error] NSEC or NSEC3 found at '"<<rr.qname.toString()<<"'. These do not belong in the database."<<endl;
numerrors++;
continue;
}
{
if(rr.ttl != sd.default_ttl)
{
- cout<<"[Warning] DNSKEY TTL of "<<rr.ttl<<" at '"<<rr.qname<<"' differs from SOA minimum of "<<sd.default_ttl<<endl;
+ cout<<"[Warning] DNSKEY TTL of "<<rr.ttl<<" at '"<<rr.qname.toString()<<"' differs from SOA minimum of "<<sd.default_ttl<<endl;
numwarnings++;
}
}
else
{
- cout<<"[Warning] DNSKEY at '"<<rr.qname<<"' in non-presigned zone will mostly be ignored and can cause problems."<<endl;
+ cout<<"[Warning] DNSKEY at '"<<rr.qname.toString()<<"' in non-presigned zone will mostly be ignored and can cause problems."<<endl;
numwarnings++;
}
}
- if(rr.qtype.getCode() == QType::URL || rr.qtype.getCode() == QType::MBOXFW) {
- cout<<"[Error] The recordtype "<<rr.qtype.getName()<<" for record '"<<rr.qname<<"' is no longer supported."<<endl;
- numerrors++;
- continue;
- }
-
- if (rr.qname[rr.qname.size()-1] == '.') {
- cout<<"[Error] Record '"<<rr.qname<<"' has a trailing dot. PowerDNS will ignore this record!"<<endl;
- numerrors++;
- }
+ // if (rr.qname[rr.qname.size()-1] == '.') {
+ // cout<<"[Error] Record '"<<rr.qname.toString()<<"' has a trailing dot. PowerDNS will ignore this record!"<<endl;
+ // numerrors++;
+ // }
if ( (rr.qtype.getCode() == QType::NS || rr.qtype.getCode() == QType::SRV || rr.qtype.getCode() == QType::MX || rr.qtype.getCode() == QType::CNAME) &&
rr.content[rr.content.size()-1] == '.') {
- cout<<"[Warning] The record "<<rr.qname<<" with type "<<rr.qtype.getName()<<" has a trailing dot in the content ("<<rr.content<<"). Your backend might not work well with this."<<endl;
+ cout<<"[Warning] The record "<<rr.qname.toString()<<" with type "<<rr.qtype.getName()<<" has a trailing dot in the content ("<<rr.content<<"). Your backend might not work well with this."<<endl;
numwarnings++;
}
if(rr.auth == 0 && rr.qtype.getCode()!=QType::NS && rr.qtype.getCode()!=QType::A && rr.qtype.getCode()!=QType::AAAA)
{
- cout<<"[Error] Following record is auth=0, run pdnssec rectify-zone?: "<<rr.qname<<" IN " <<rr.qtype.getName()<< " " << rr.content<<endl;
+ cout<<"[Error] Following record is auth=0, run pdnssec rectify-zone?: "<<rr.qname.toString()<<" IN " <<rr.qtype.getName()<< " " << rr.content<<endl;
numerrors++;
}
}
- for(set<string>::const_iterator i = cnames.begin(); i != cnames.end(); i++) {
- if (noncnames.find(*i) != noncnames.end()) {
- cout<<"[Error] CNAME "<<*i<<" found, but other records with same label exist."<<endl;
+ for(auto &i: cnames) {
+ if (noncnames.find(i) != noncnames.end()) {
+ cout<<"[Error] CNAME "<<i.toString()<<" found, but other records with same label exist."<<endl;
numerrors++;
}
}
if(!hasNsAtApex) {
- cout<<"[Error] No NS record at zone apex in zone '"<<zone<<"'"<<endl;
+ cout<<"[Error] No NS record at zone apex in zone '"<<zone.toString()<<"'"<<endl;
numerrors++;
}
- cout<<"Checked "<<numrecords<<" records of '"<<zone<<"', "<<numerrors<<" errors, "<<numwarnings<<" warnings."<<endl;
+ for(const auto &qname : checkglue) {
+ if (!glue.count(qname)) {
+ cerr<<"[Warning] Missing glue for '"<<qname.toString()<<"' in zone '"<<zone.toString()<<"'"<<endl;
+ numwarnings++;
+ }
+ }
+
+ cout<<"Checked "<<numrecords<<" records of '"<<zone.toString()<<"', "<<numerrors<<" errors, "<<numwarnings<<" warnings."<<endl;
return numerrors;
}
return 0;
}
-int increaseSerial(const string& zone, DNSSECKeeper &dk)
+int increaseSerial(const DNSName& zone, DNSSECKeeper &dk)
{
UeberBackend B("default");
SOAData sd;
- sd.db=(DNSBackend*)-1;
- if(!B.getSOA(zone, sd)) {
- cout<<"No SOA for zone '"<<zone<<"'"<<endl;
+ if(!B.getSOAUncached(zone, sd)) {
+ cout<<"No SOA for zone '"<<zone.toString()<<"'"<<endl;
return -1;
}
}
if (rrs.size() > 1) {
- cerr<<rrs.size()<<" SOA records found for "<<zone<<"!"<<endl;
+ cerr<<rrs.size()<<" SOA records found for "<<zone.toString()<<"!"<<endl;
return -1;
}
if (rrs.size() < 1) {
- cerr<<zone<<" not found!"<<endl;
+ cerr<<zone.toString()<<" not found!"<<endl;
}
if (soaEditKind.empty()) {
if(!narrow) {
string hashed=toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, rrs[0].qname));
if(g_verbose)
- cerr<<"'"<<rrs[0].qname<<"' -> '"<< hashed <<"'"<<endl;
+ cerr<<"'"<<rrs[0].qname.toString()<<"' -> '"<< hashed <<"'"<<endl;
sd.db->updateDNSSECOrderAndAuthAbsolute(sd.domain_id, rrs[0].qname, hashed, 1);
}
else {
sd.db->commitTransaction();
- cout<<"SOA serial for zone "<<zone<<" set to "<<sd.serial<<endl;
+ cout<<"SOA serial for zone "<<zone.toString()<<" set to "<<sd.serial<<endl;
return 0;
}
-int deleteZone(const string &zone) {
+int deleteZone(const DNSName &zone) {
UeberBackend B;
DomainInfo di;
if (! B.getDomainInfo(zone, di)) {
- cerr<<"Domain '"<<zone<<"' not found!"<<endl;
+ cerr<<"Domain '"<<zone.toString()<<"' not found!"<<endl;
return 1;
}
if(di.backend->deleteDomain(zone))
return 0;
- cerr<<"Failed to delete domain '"+zone+"'"<<endl;;
+ cerr<<"Failed to delete domain '"<<zone.toString()<<"'"<<endl;;
+ return 1;
+}
+
+int listZone(const DNSName &zone) {
+ UeberBackend B;
+ DomainInfo di;
+
+ if (! B.getDomainInfo(zone, di)) {
+ cerr<<"Domain '"<<zone.toString()<<"' not found!"<<endl;
+ return 1;
+ }
+ di.backend->list(zone, di.id);
+ DNSResourceRecord rr;
+ while(di.backend->get(rr)) {
+ if(rr.qtype.getCode()) {
+ if ( (rr.qtype.getCode() == QType::NS || rr.qtype.getCode() == QType::SRV || rr.qtype.getCode() == QType::MX || rr.qtype.getCode() == QType::CNAME) && !rr.content.empty() && rr.content[rr.content.size()-1] != '.')
+ rr.content.append(1, '.');
+
+ cout<<rr.qname.toString()<<".\t"<<rr.ttl<<"\tIN\t"<<rr.qtype.getName()<<"\t"<<rr.content<<endl;
+ }
+ }
+ return 0;
+}
+
+int loadZone(DNSName zone, const string& fname) {
+ UeberBackend B;
+ DomainInfo di;
+
+ if (B.getDomainInfo(zone, di)) {
+ cerr<<"Domain '"<<zone.toString()<<"' exists already, replacing contents"<<endl;
+ }
+ else {
+ cerr<<"Creating '"<<zone.toString()<<"'"<<endl;
+ B.createDomain(zone);
+
+ if(!B.getDomainInfo(zone, di)) {
+ cerr<<"Domain '"<<zone.toString()<<"' was not created!"<<endl;
+ return 1;
+ }
+ }
+ DNSBackend* db = di.backend;
+ ZoneParserTNG zpt(fname, zone);
+
+ DNSResourceRecord rr;
+ if(!db->startTransaction(zone, di.id)) {
+ cerr<<"Unable to start transaction for load of zone '"<<zone.toString()<<"'"<<endl;
+ return 1;
+ }
+ rr.domain_id=di.id;
+ while(zpt.get(rr)) {
+ if(!rr.qname.isPartOf(zone) && rr.qname!=zone) {
+ cerr<<"File contains record named '"<<rr.qname.toString()<<"' which is not part of zone '"<<zone.toString()<<"'"<<endl;
+ return 1;
+ }
+ db->feedRecord(rr);
+ }
+ db->commitTransaction();
+ return 0;
+}
+
+int createZone(const DNSName &zone) {
+ UeberBackend B;
+ DomainInfo di;
+ if (B.getDomainInfo(zone, di)) {
+ cerr<<"Domain '"<<zone.toString()<<"' exists already"<<endl;
+ return 1;
+ }
+ cerr<<"Creating '"<<zone.toString()<<"'"<<endl;
+ B.createDomain(zone);
+
+ if(!B.getDomainInfo(zone, di)) {
+ cerr<<"Domain '"<<zone.toString()<<"' was not created!"<<endl;
+ return 1;
+ }
return 1;
}
+
int listAllZones(const string &type="") {
int kindFilter = -1;
int count = 0;
for (vector<DomainInfo>::const_iterator di=domains.begin(); di != domains.end(); di++) {
if (di->kind == kindFilter || kindFilter == -1) {
- cout<<di->zone<<endl;
+ cout<<di->zone.toString()<<endl;
count++;
}
}
RRSIGRecordContent rrc;
DSRecordContent dsrc;
vector<shared_ptr<DNSRecordContent> > toSign;
- string qname, apex;
+ DNSName qname, apex;
dsrc.d_digesttype=0;
while(zpt.get(rr)) {
if(rr.qtype.getCode() == QType::DNSKEY) {
string msg = getMessageForRRSET(qname, rrc, toSign);
cerr<<"Verify: "<<DNSCryptoKeyEngine::makeFromPublicKeyString(drc.d_algorithm, drc.d_key)->verify(msg, rrc.d_signature)<<endl;
if(dsrc.d_digesttype) {
- cerr<<"Calculated DS: "<<apex<<" IN DS "<<makeDSFromDNSKey(apex, drc, dsrc.d_digesttype).getZoneRepresentation()<<endl;
- cerr<<"Original DS: "<<apex<<" IN DS "<<dsrc.getZoneRepresentation()<<endl;
+ cerr<<"Calculated DS: "<<apex.toString()<<" IN DS "<<makeDSFromDNSKey(apex, drc, dsrc.d_digesttype).getZoneRepresentation()<<endl;
+ cerr<<"Original DS: "<<apex.toString()<<" IN DS "<<dsrc.getZoneRepresentation()<<endl;
}
#if 0
DNSCryptoKeyEngine*key=DNSCryptoKeyEngine::makeFromISCString(drc, "Private-key-format: v1.2\n"
#endif
}
-bool disableDNSSECOnZone(DNSSECKeeper& dk, const string& zone)
+bool disableDNSSECOnZone(DNSSECKeeper& dk, const DNSName& zone)
{
UeberBackend B("default");
DomainInfo di;
DNSSECKeeper::keyset_t keyset=dk.getKeys(zone);
if(keyset.empty()) {
- cerr << "No keys for zone '"<<zone<<"'."<<endl;
+ cerr << "No keys for zone '"<<zone.toString()<<"'."<<endl;
}
else {
BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, keyset) {
dk.unsetPresigned(zone);
return true;
}
-bool showZone(DNSSECKeeper& dk, const std::string& zone)
+bool showZone(DNSSECKeeper& dk, const DNSName& zone)
{
UeberBackend B("default");
DomainInfo di;
cout <<"Zone is " << (dk.isPresigned(zone) ? "" : "not ") << "presigned"<<endl;
if(keyset.empty()) {
- cerr << "No keys for zone '"<<zone<<"'."<<endl;
+ cerr << "No keys for zone '"<<zone.toString()<<"'."<<endl;
}
else {
if(!haveNSEC3)
BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, keyset) {
string algname;
algorithm2name(value.first.d_algorithm, algname);
+ if (value.first.getKey()->getBits() < 1) {
+ cout<<"ID = "<<value.second.id<<" ("<<(value.second.keyOrZone ? "KSK" : "ZSK")<<") <key missing or defunct>" <<endl;
+ continue;
+ }
cout<<"ID = "<<value.second.id<<" ("<<(value.second.keyOrZone ? "KSK" : "ZSK")<<"), tag = "<<value.first.getDNSKEY().getTag();
cout<<", algo = "<<(int)value.first.d_algorithm<<", bits = "<<value.first.getKey()->getBits()<<"\tActive: "<<value.second.active<< " ( " + algname + " ) "<<endl;
if(value.second.keyOrZone || ::arg().mustDo("direct-dnskey") || g_verbose)
- cout<<(value.second.keyOrZone ? "KSK" : "ZSK")<<" DNSKEY = "<<zone<<" IN DNSKEY "<< value.first.getDNSKEY().getZoneRepresentation() << " ; ( " + algname + " )" << endl;
+ cout<<(value.second.keyOrZone ? "KSK" : "ZSK")<<" DNSKEY = "<<zone.toString()<<" IN DNSKEY "<< value.first.getDNSKEY().getZoneRepresentation() << " ; ( " + algname + " )" << endl;
if(value.second.keyOrZone || g_verbose) {
- cout<<"DS = "<<zone<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 1).getZoneRepresentation() << " ; ( SHA1 digest )" << endl;
- cout<<"DS = "<<zone<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 2).getZoneRepresentation() << " ; ( SHA256 digest )" << endl;
+ cout<<"DS = "<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 1).getZoneRepresentation() << " ; ( SHA1 digest )" << endl;
+ cout<<"DS = "<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 2).getZoneRepresentation() << " ; ( SHA256 digest )" << endl;
try {
string output=makeDSFromDNSKey(zone, value.first.getDNSKEY(), 3).getZoneRepresentation();
- cout<<"DS = "<<zone<<" IN DS "<< output << " ; ( GOST R 34.11-94 digest )" << endl;
+ cout<<"DS = "<<zone.toString()<<" IN DS "<< output << " ; ( GOST R 34.11-94 digest )" << endl;
}
catch(...)
{
}
try {
string output=makeDSFromDNSKey(zone, value.first.getDNSKEY(), 4).getZoneRepresentation();
- cout<<"DS = "<<zone<<" IN DS "<< output << " ; ( SHA-384 digest )" << endl;
+ cout<<"DS = "<<zone.toString()<<" IN DS "<< output << " ; ( SHA-384 digest )" << endl;
}
catch(...)
{
return true;
}
-bool secureZone(DNSSECKeeper& dk, const std::string& zone)
+bool secureZone(DNSSECKeeper& dk, const DNSName& zone)
{
// parse attribute
vector<string> k_algos;
}
if(dk.isSecuredZone(zone)) {
- cerr << "Zone '"<<zone<<"' already secure, remove keys with pdnssec remove-zone-key if needed"<<endl;
+ cerr << "Zone '"<<zone.toString()<<"' already secure, remove keys with pdnssec remove-zone-key if needed"<<endl;
return false;
}
DomainInfo di;
UeberBackend B("default");
if(!B.getDomainInfo(zone, di) || !di.backend) { // di.backend and B are mostly identical
- cout<<"Can't find a zone called '"<<zone<<"'"<<endl;
+ cout<<"Can't find a zone called '"<<zone.toString()<<"'"<<endl;
return false;
}
if(di.kind == DomainInfo::Slave)
{
cout<<"Warning! This is a slave domain! If this was a mistake, please run"<<endl;
- cout<<"pdnssec disable-dnssec "<<zone<<" right now!"<<endl;
+ cout<<"pdnssec disable-dnssec "<<zone.toString()<<" right now!"<<endl;
}
if (k_size)
// run secure-zone with first default algorith, then add keys
if(!dk.secureZone(zone, shorthand2algorithm(k_algos[0]), k_size)) {
- cerr<<"No backend was able to secure '"<<zone<<"', most likely because no DNSSEC"<<endl;
+ cerr<<"No backend was able to secure '"<<zone.toString()<<"', most likely because no DNSSEC"<<endl;
cerr<<"capable backends are loaded, or because the backends have DNSSEC disabled."<<endl;
cerr<<"For the Generic SQL backends, set the 'gsqlite3-dnssec', 'gmysql-dnssec' or"<<endl;
cerr<<"'gpgsql-dnssec' flag. Also make sure the schema has been updated for DNSSEC!"<<endl;
DNSSECKeeper::keyset_t zskset=dk.getKeys(zone, false);
if(!zskset.empty()) {
- cerr<<"There were ZSKs already for zone '"<<zone<<"', no need to add more"<<endl;
+ cerr<<"There were ZSKs already for zone '"<<zone.toString()<<"', no need to add more"<<endl;
return false;
}
// rectifyZone(dk, zone);
// showZone(dk, zone);
- cout<<"Zone "<<zone<<" secured"<<endl;
+ cout<<"Zone "<<zone.toString()<<" secured"<<endl;
return true;
}
-void testSchema(DNSSECKeeper& dk, const std::string& zone)
+void testSchema(DNSSECKeeper& dk, const DNSName& zone)
{
cout<<"Note: test-schema will try to create the zone, but it will not remove it."<<endl;
cout<<"Please clean up after this."<<endl;
UeberBackend B("default");
cout<<"Picking first backend - if this is not what you want, edit launch line!"<<endl;
DNSBackend *db = B.backends[0];
- cout<<"Creating slave domain "<<zone<<endl;
+ cout<<"Creating slave domain "<<zone.toString()<<endl;
db->createSlaveDomain("127.0.0.1", zone, "", "_testschema");
cout<<"Slave domain created"<<endl;
cout<<"Rectifying zone"<<endl;
rectifyZone(dk, zone);
cout<<"Checking underscore ordering"<<endl;
- string before, after;
+ DNSName before, after;
db->getBeforeAndAfterNames(di.id, zone, "z."+zone, before, after);
- cout<<"got '"<<before<<"' < 'z."<<zone<<"' < '"<<after<<"'"<<endl;
+ cout<<"got '"<<before.toString()<<"' < 'z."<<zone.toString()<<"' < '"<<after.toString()<<"'"<<endl;
if(before != "_underscore."+zone)
{
- cout<<"before is wrong, got '"<<before<<"', expected '_underscore."<<zone<<"', aborting"<<endl;
+ cout<<"before is wrong, got '"<<before.toString()<<"', expected '_underscore."<<zone.toString()<<"', aborting"<<endl;
return;
}
if(after != zone)
{
- cout<<"after is wrong, got '"<<after<<"', expected '"<<zone<<"', aborting"<<endl;
+ cout<<"after is wrong, got '"<<after.toString()<<"', expected '"<<zone.toString()<<"', aborting"<<endl;
return;
}
cout<<"[+] ordername sorting is correct for names starting with _"<<endl;
cout<<endl;
- cout<<"End of tests, please remove "<<zone<<" from domains+records"<<endl;
+ cout<<"End of tests, please remove "<<zone.toString()<<" from domains+records"<<endl;
}
int main(int argc, char** argv)
cerr<<"add-zone-key ZONE zsk|ksk [bits] [active|passive]"<<endl;
cerr<<" [rsasha1|rsasha256|rsasha512|gost|ecdsa256|ecdsa384]"<<endl;
cerr<<" Add a ZSK or KSK to zone and specify algo&bits"<<endl;
+ cerr<<"b2b-migrate old new Move all data from one backend to another"<<endl;
cerr<<"bench-db [filename] Bench database backend with queries, one domain per line"<<endl;
cerr<<"check-zone ZONE Check a zone for correctness"<<endl;
cerr<<"check-all-zones Check all zones for correctness"<<endl;
cerr<<"create-bind-db FNAME Create DNSSEC db for BIND backend (bind-dnssec-db)"<<endl;
+ cerr<<"create-zone ZONE Create empty zone ZONE"<<endl;
cerr<<"deactivate-tsig-key ZONE NAME [master|slave]"<<endl;
cerr<<" Disable TSIG key for a zone"<<endl;
cerr<<"deactivate-zone-key ZONE KEY-ID Deactivate the key with key id KEY-ID in ZONE"<<endl;
cerr<<"get-meta ZONE [kind kind ..] Get zone metadata. If no KIND given, lists all known"<<endl;
cerr<<"hash-zone-record ZONE RNAME Calculate the NSEC3 hash for RNAME in ZONE"<<endl;
#ifdef HAVE_P11KIT1
- cerr<<"hsm assign zone zsk|ksk module slot pin label"<<endl<<
+ cerr<<"hsm assign zone algorithm ksk|zsk module slot pin label"<<endl<<
" Assign a hardware signing module to a ZONE"<<endl;
- cerr<<"hsm create-key zone [bits] Create a key using hardware signing module for ZONE (use assign first)"<<endl;
+ cerr<<"hsm create-key zone key-id [bits] Create a key using hardware signing module for ZONE (use assign first)"<<endl;
cerr<<" bits defaults to 2048"<<endl;
#endif
cerr<<"increase-serial ZONE Increases the SOA-serial by 1. Uses SOA-EDIT"<<endl;
cerr<<"import-tsig-key NAME ALGORITHM KEY Import TSIG key"<<endl;
cerr<<"import-zone-key ZONE FILE Import from a file a private key, ZSK or KSK"<<endl;
cerr<<" [active|passive][ksk|zsk] Defaults to KSK and active"<<endl;
+ cerr<<"load-zone ZONE FILE Load ZONE from FILE, possibly creating zone or atomically"<<endl;
+ cerr<<" replacing contents"<<endl;
+ cerr<<"list-zone ZONE List zone contents"<<endl;
cerr<<"list-all-zones [master|slave|native]"<<endl;
- cerr<<" List all zones"<<endl;;
+ cerr<<" List all zone names"<<endl;;
cerr<<"list-tsig-keys List all TSIG keys"<<endl;
cerr<<"rectify-zone ZONE [ZONE ..] Fix up DNSSEC fields (order, auth)"<<endl;
cerr<<"rectify-all-zones Rectify all zones."<<endl;
SSQLite3 db(cmds[1], true); // create=ok
vector<string> statements;
stringtok(statements, sqlCreate, ";");
- BOOST_FOREACH(const string& statement, statements)
- db.doCommand(statement);
+ BOOST_FOREACH(const string& statement, statements) {
+ db.execute(statement);
+ }
}
catch(SSqlException& se) {
throw PDNSException("Error creating database in BIND backend: "+se.txtReason());
}
exit(deleteZone(cmds[1]));
}
+ else if(cmds[0] == "create-zone") {
+ if(cmds.size() != 2) {
+ cerr<<"Syntax: pdnssec create-zone ZONE"<<endl;
+ return 0;
+ }
+ exit(createZone(cmds[1]));
+ }
+ else if(cmds[0] == "list-zone") {
+ if(cmds.size() != 2) {
+ cerr<<"Syntax: pdnssec list-zone ZONE"<<endl;
+ return 0;
+ }
+ if(cmds[1]==".")
+ cmds[1].clear();
+
+ exit(listZone(cmds[1]));
+ }
+ else if(cmds[0] == "load-zone") {
+ if(cmds.size() != 3) {
+ cerr<<"Syntax: pdnssec load-zone ZONE FILENAME"<<endl;
+ return 0;
+ }
+ if(cmds[1]==".")
+ cmds[1].clear();
+
+ exit(loadZone(cmds[1], cmds[2]));
+ }
else if(cmds[0] == "secure-zone") {
if(cmds.size() < 2) {
cerr << "Syntax: pdnssec secure-zone ZONE"<<endl;
unsigned int zonesSecured=0, zoneErrors=0;
BOOST_FOREACH(DomainInfo di, domainInfo) {
if(!dk.isSecuredZone(di.zone)) {
- cout<<"Securing "<<di.zone<<": ";
+ cout<<"Securing "<<di.zone.toString()<<": ";
if (secureZone(dk, di.zone)) {
zonesSecured++;
if (cmds.size() == 2) {
bool narrow = cmds.size() > 3 && cmds[3]=="narrow";
NSEC3PARAMRecordContent ns3pr(nsec3params);
- string zone=cmds[1];
+ DNSName zone(cmds[1]);
if(!dk.isSecuredZone(zone)) {
- cerr<<"Zone '"<<zone<<"' is not secured, can't set NSEC3 parameters"<<endl;
+ cerr<<"Zone '"<<zone.toString()<<"' is not secured, can't set NSEC3 parameters"<<endl;
exit(EXIT_FAILURE);
}
dk.setNSEC3PARAM(zone, ns3pr, narrow);
cerr<<"Syntax: pdnssec hash-zone-record ZONE RNAME"<<endl;
return 0;
}
- string& zone=cmds[1];
+ DNSName zone(cmds[1]);
string& record=cmds[2];
NSEC3PARAMRecordContent ns3pr;
bool narrow;
if(!dk.getNSEC3PARAM(zone, &ns3pr, &narrow)) {
- cerr<<"The '"<<zone<<"' zone does not use NSEC3"<<endl;
+ cerr<<"The '"<<zone.toString()<<"' zone does not use NSEC3"<<endl;
return 0;
}
if(narrow) {
- cerr<<"The '"<<zone<<"' zone uses narrow NSEC3, but calculating hash anyhow"<<endl;
+ cerr<<"The '"<<zone.toString()<<"' zone uses narrow NSEC3, but calculating hash anyhow"<<endl;
}
cout<<toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, record))<<endl;
UeberBackend B("default");
if (B.getTSIGKeys(keys)) {
BOOST_FOREACH(const struct TSIGKey &key, keys) {
- cout << key.name << " " << key.algorithm << " " << key.key << endl;
+ cout << key.name.toString() << " " << key.algorithm.toString() << " " << key.key << endl;
}
}
return 0;
if (cmds[1] == "assign") {
DNSCryptoKeyEngine::storvector_t storvect;
DomainInfo di;
+ std::vector<DNSBackend::KeyData> keys;
if (cmds.size() < 9) {
std::cout << "Usage: pdnssec hsm assign zone algorithm ksk|zsk module slot pin label" << std::endl;
}
int algorithm = shorthand2algorithm(cmds[3]);
+ if (algorithm<0) {
+ cerr << "Unable to use unknown algorithm '" << cmds[3] << "'" << std::endl;
+ return 1;
+ }
+
int id;
bool keyOrZone = (cmds[4] == "ksk" ? true : false);
string module = cmds[5];
dpk.d_flags = (keyOrZone ? 257 : 256);
dpk.setKey(shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromISCString(drc, iscString.str())));
+ // make sure this key isn't being reused.
+ B.getDomainKeys(zone, 0, keys);
+ id = -1;
+
+ BOOST_FOREACH(DNSBackend::KeyData& kd, keys) {
+ if (kd.content == iscString.str()) {
+ // it's this one, I guess...
+ id = kd.id;
+ break;
+ }
+ }
+
+ if (id > -1) {
+ cerr << "You have already assigned this key with ID=" << id << std::endl;
+ return 1;
+ }
+
if (!(id = dk.addKey(zone, dpk))) {
cerr << "Unable to assign module slot to zone" << std::endl;
return 1;
}
+ // figure out key id.
+
+ B.getDomainKeys(zone, 0, keys);
+
+ // validate which one got the key...
+ BOOST_FOREACH(DNSBackend::KeyData& kd, keys) {
+ if (kd.content == iscString.str()) {
+ // it's this one, I guess...
+ id = kd.id;
+ break;
+ }
+ }
+
cerr << "Module " << module << " slot " << slot << " assigned to " << zone << " with key id " << id << endl;
+
return 0;
} else if (cmds[1] == "create-key") {
cerr<<"PKCS#11 support not enabled"<<endl;
return 1;
#endif
+ } else if (cmds[0] == "b2b-migrate") {
+ if (cmds.size() < 3) {
+ cerr<<"Usage: b2b-migrate old new"<<endl;
+ return 1;
+ }
+
+ DNSBackend *src,*tgt;
+ src = tgt = NULL;
+
+ for(DNSBackend *b : BackendMakers().all()) {
+ if (b->getPrefix() == cmds[1]) src = b;
+ if (b->getPrefix() == cmds[2]) tgt = b;
+ }
+ if (!src) {
+ cerr<<"Unknown source backend '"<<cmds[1]<<"'"<<endl;
+ return 1;
+ }
+ if (!tgt) {
+ cerr<<"Unknown target backend '"<<cmds[2]<<"'"<<endl;
+ return 1;
+ }
+
+ cout<<"Moving zone(s) from "<<src->getPrefix()<<" to "<<tgt->getPrefix()<<endl;
+
+ vector<DomainInfo> domains;
+
+ tgt->getAllDomains(&domains, true);
+ if (domains.size()>0)
+ throw PDNSException("Target backend has domain(s), please clean it first");
+
+ src->getAllDomains(&domains, true);
+ // iterate zones
+ for(const DomainInfo& di: domains) {
+ size_t nr,nc,nm,nk;
+ DNSResourceRecord rr;
+ cout<<"Processing '"<<di.zone<<"'"<<endl;
+ // create zone
+ if (!tgt->createDomain(di.zone)) throw PDNSException("Failed to create zone");
+ tgt->setKind(di.zone, di.kind);
+ tgt->setAccount(di.zone,di.account);
+ for(const string& master: di.masters) {
+ tgt->setMaster(di.zone, master);
+ }
+ // move records
+ if (!src->list(di.zone, di.id, true)) throw PDNSException("Failed to list records");
+ nr=0;
+ while(src->get(rr)) {
+ if (!tgt->feedRecord(rr)) throw PDNSException("Failed to feed record");
+ nr++;
+ }
+ // move comments
+ nc=0;
+ if (src->listComments(di.id)) {
+ Comment c;
+ while(src->getComment(c)) {
+ tgt->feedComment(c);
+ nc++;
+ }
+ }
+ // move metadata
+ nm=0;
+ std::map<std::string, std::vector<std::string> > meta;
+ if (src->getAllDomainMetadata(di.zone, meta)) {
+ std::map<std::string, std::vector<std::string> >::iterator i;
+ for(i=meta.begin(); i != meta.end(); i++) {
+ if (!tgt->setDomainMetadata(di.zone, i->first, i->second)) throw PDNSException("Failed to feed domain metadata");
+ nm++;
+ }
+ }
+ // move keys
+ nk=0;
+ std::vector<DNSBackend::KeyData> keys;
+ if (src->getDomainKeys(di.zone, 0, keys)) {
+ for(const DNSBackend::KeyData& k: keys) {
+ tgt->addDomainKey(di.zone, k);
+ nk++;
+ }
+ }
+ cout<<"Moved "<<nr<<" record(s), "<<nc<<" comment(s), "<<nm<<" metadata(s) and "<<nk<<" cryptokey(s)"<<endl;
+ }
+
+ int ntk=0;
+ // move tsig keys
+ std::vector<struct TSIGKey> tkeys;
+ if (src->getTSIGKeys(tkeys)) {
+ for(const struct TSIGKey& tk: tkeys) {
+ if (!tgt->setTSIGKey(tk.name, tk.algorithm, tk.key)) throw PDNSException("Failed to feed TSIG key");
+ ntk++;
+ }
+ }
+ cout<<"Moved "<<ntk<<" TSIG key(s)"<<endl;
+
+ cout<<"Remember to drop the old backend and run rectify-all-zones"<<endl;
+
+ return 0;
} else {
cerr<<"Unknown command '"<<cmds[0] <<"'"<< endl;
return 1;
cerr<<"Error: "<<e.what()<<endl;
return 1;
}
-
+catch(...)
+{
+ cerr<<"Caught an unknown exception"<<endl;
+ return 1;
+}