5 #include "dnsseckeeper.hh"
6 #include "dnssecinfra.hh"
11 #include <boost/program_options.hpp>
12 #include <boost/assign/std/vector.hpp>
13 #include <boost/assign/list_of.hpp>
14 #include "tsigutils.hh"
15 #include "dnsbackend.hh"
16 #include "ueberbackend.hh"
17 #include "arguments.hh"
18 #include "auth-packetcache.hh"
19 #include "auth-querycache.hh"
20 #include "zoneparser-tng.hh"
21 #include "signingpipe.hh"
22 #include "dns_random.hh"
23 #include "ipcipher.hh"
25 #include <termios.h> //termios, TCSANOW, ECHO, ICANON
26 #include "opensslsigners.hh"
31 #include "ssqlite3.hh"
32 #include "bind-dnssec.schema.sqlite3.sql.h"
39 namespace po
= boost::program_options
;
40 po::variables_map g_vm
;
42 string s_programname
="pdns";
54 static void loadMainConfig(const std::string
& configdir
)
56 ::arg().set("config-dir","Location of configuration directory (pdns.conf)")=configdir
;
57 ::arg().set("default-ttl","Seconds a result is valid if not set otherwise")="3600";
58 ::arg().set("launch","Which backends to launch");
59 ::arg().set("dnssec","if we should do dnssec")="true";
60 ::arg().set("config-name","Name of this virtual configuration - will rename the binary image")=g_vm
["config-name"].as
<string
>();
61 ::arg().setCmd("help","Provide a helpful message");
62 ::arg().set("load-modules","Load this module - supply absolute or relative path")="";
63 //::arg().laxParse(argc,argv);
65 if(::arg().mustDo("help")) {
66 cout
<<"syntax:"<<endl
<<endl
;
67 cout
<<::arg().helpstring(::arg()["help"])<<endl
;
71 if(::arg()["config-name"]!="")
72 s_programname
+="-"+::arg()["config-name"];
74 string configname
=::arg()["config-dir"]+"/"+s_programname
+".conf";
75 cleanSlashes(configname
);
77 ::arg().set("resolver","Use this resolver for ALIAS and the internal stub resolver")="no";
78 ::arg().set("default-ksk-algorithm","Default KSK algorithm")="ecdsa256";
79 ::arg().set("default-ksk-size","Default KSK size (0 means default)")="0";
80 ::arg().set("default-zsk-algorithm","Default ZSK algorithm")="";
81 ::arg().set("default-zsk-size","Default ZSK size (0 means default)")="0";
82 ::arg().set("default-soa-edit","Default SOA-EDIT value")="";
83 ::arg().set("default-soa-edit-signed","Default SOA-EDIT value for signed zones")="";
84 ::arg().set("max-ent-entries", "Maximum number of empty non-terminals in a zone")="100000";
85 ::arg().set("module-dir","Default directory for modules")=PKGLIBDIR
;
86 ::arg().set("entropy-source", "If set, read entropy from this file")="/dev/urandom";
87 ::arg().setSwitch("query-logging","Hint backends that queries should be logged")="no";
88 ::arg().set("loglevel","Amount of logging. Higher is more.")="3";
89 ::arg().setSwitch("direct-dnskey","Fetch DNSKEY, CDS and CDNSKEY RRs from backend during DNSKEY or CDS/CDNSKEY synthesis")="no";
90 ::arg().set("max-nsec3-iterations","Limit the number of NSEC3 hash iterations")="500"; // RFC5155 10.3
91 ::arg().set("max-signature-cache-entries", "Maximum number of signatures cache entries")="";
92 ::arg().set("rng", "Specify random number generator to use. Valid values are auto,sodium,openssl,getrandom,arc4random,urandom.")="auto";
93 ::arg().set("max-generate-steps", "Maximum number of $GENERATE steps when loading a zone from a file")="0";
94 ::arg().laxFile(configname
.c_str());
96 if(!::arg()["load-modules"].empty()) {
97 vector
<string
> modules
;
99 stringtok(modules
,::arg()["load-modules"], ", ");
100 if (!UeberBackend::loadModules(modules
, ::arg()["module-dir"])) {
105 g_log
.toConsole(Logger::Error
); // so we print any errors
106 BackendMakers().launch(::arg()["launch"]); // vrooooom!
107 if(::arg().asNum("loglevel") >= 3) // so you can't kill our errors
108 g_log
.toConsole((Logger::Urgency
)::arg().asNum("loglevel"));
110 //cerr<<"Backend: "<<::arg()["launch"]<<", '" << ::arg()["gmysql-dbname"] <<"'" <<endl;
112 S
.declare("qsize-q","Number of questions waiting for database attention");
114 ::arg().set("max-cache-entries", "Maximum number of cache entries")="1000000";
115 ::arg().set("cache-ttl","Seconds to store packets in the PacketCache")="20";
116 ::arg().set("negquery-cache-ttl","Seconds to store negative query results in the QueryCache")="60";
117 ::arg().set("query-cache-ttl","Seconds to store query results in the QueryCache")="20";
118 ::arg().set("default-soa-name","name to insert in the SOA record if none set in the backend")="a.misconfigured.powerdns.server";
119 ::arg().set("default-soa-mail","mail address to insert in the SOA record if none set in the backend")="";
120 ::arg().set("soa-refresh-default","Default SOA refresh")="10800";
121 ::arg().set("soa-retry-default","Default SOA retry")="3600";
122 ::arg().set("soa-expire-default","Default SOA expire")="604800";
123 ::arg().set("soa-minimum-ttl","Default SOA minimum ttl")="3600";
124 ::arg().set("chroot","Switch to this chroot jail")="";
125 ::arg().set("dnssec-key-cache-ttl","Seconds to cache DNSSEC keys from the database")="30";
126 ::arg().set("domain-metadata-cache-ttl","Seconds to cache domain metadata from the database")="60";
128 // Keep this line below all ::arg().set() statements
129 if (! ::arg().laxFile(configname
.c_str()))
130 cerr
<<"Warning: unable to read configuration file '"<<configname
<<"': "<<stringerror()<<endl
;
132 #ifdef HAVE_LIBSODIUM
133 if (sodium_init() == -1) {
134 cerr
<<"Unable to initialize sodium crypto library"<<endl
;
139 /* init rng before chroot */
142 if (!::arg()["chroot"].empty()) {
143 if (chroot(::arg()["chroot"].c_str())<0 || chdir("/") < 0) {
144 cerr
<<"Unable to chroot to '"+::arg()["chroot"]+"': "<<strerror (errno
)<<endl
;
152 static bool rectifyZone(DNSSECKeeper
& dk
, const DNSName
& zone
, bool quiet
= false, bool rectifyTransaction
= true)
156 bool ret
= dk
.rectifyZone(zone
, error
, output
, rectifyTransaction
);
157 if (!quiet
|| !ret
) {
158 // When quiet, only print output if there was an error
159 if (!output
.empty()) {
162 if (!ret
&& !error
.empty()) {
169 static void dbBench(const std::string
& fname
)
171 ::arg().set("query-cache-ttl")="0";
172 ::arg().set("negquery-cache-ttl")="0";
173 UeberBackend
B("default");
175 vector
<string
> domains
;
177 ifstream
ifs(fname
.c_str());
179 cerr
<<"Could not open '"<<fname
<<"' for reading domain names to query"<<endl
;
182 while(getline(ifs
,line
)) {
184 domains
.push_back(line
);
188 domains
.push_back("powerdns.com");
194 unsigned int hits
=0, misses
=0;
195 for(; n
< 10000; ++n
) {
196 DNSName
domain(domains
[dns_random(domains
.size())]);
197 B
.lookup(QType(QType::NS
), domain
, -1);
201 B
.lookup(QType(QType::A
), DNSName(std::to_string(random()))+domain
, -1);
207 cout
<<0.001*dt
.udiff()/n
<<" millisecond/lookup"<<endl
;
208 cout
<<"Retrieved "<<hits
<<" records, did "<<misses
<<" queries which should have no match"<<endl
;
209 cout
<<"Packet cache reports: "<<S
.read("query-cache-hit")<<" hits (should be 0) and "<<S
.read("query-cache-miss") <<" misses"<<endl
;
212 static bool rectifyAllZones(DNSSECKeeper
&dk
, bool quiet
= false)
214 UeberBackend
B("default");
215 vector
<DomainInfo
> domainInfo
;
218 B
.getAllDomains(&domainInfo
);
219 for(DomainInfo di
: domainInfo
) {
221 cerr
<<"Rectifying "<<di
.zone
<<": ";
223 if (!rectifyZone(dk
, di
.zone
, quiet
)) {
228 cout
<<"Rectified "<<domainInfo
.size()<<" zones."<<endl
;
233 static int checkZone(DNSSECKeeper
&dk
, UeberBackend
&B
, const DNSName
& zone
, const vector
<DNSResourceRecord
>* suppliedrecords
=0)
235 uint64_t numerrors
=0, numwarnings
=0;
239 if (!B
.getDomainInfo(zone
, di
)) {
240 cout
<<"[Error] Unable to get domain information for zone '"<<zone
<<"'"<<endl
;
243 } catch(const PDNSException
&e
) {
244 if (di
.kind
== DomainInfo::Slave
) {
245 cout
<<"[Error] non-IP address for masters: "<<e
.reason
<<endl
;
251 if(!B
.getSOAUncached(zone
, sd
)) {
252 cout
<<"[Error] No SOA record present, or active, in zone '"<<zone
<<"'"<<endl
;
254 cout
<<"Checked 0 records of '"<<zone
<<"', "<<numerrors
<<" errors, 0 warnings."<<endl
;
258 NSEC3PARAMRecordContent ns3pr
;
260 bool haveNSEC3
= dk
.getNSEC3PARAM(zone
, &ns3pr
, &narrow
);
261 bool isOptOut
=(haveNSEC3
&& ns3pr
.d_flags
);
263 bool isSecure
=dk
.isSecuredZone(zone
);
264 bool presigned
=dk
.isPresigned(zone
);
265 vector
<string
> checkKeyErrors
;
266 bool validKeys
=dk
.checkKeys(zone
, &checkKeyErrors
);
269 if(isSecure
&& zone
.wirelength() > 222) {
271 cout
<<"[Error] zone '" << zone
<< "' has NSEC3 semantics but is too long to have the hash prepended. Zone name is " << zone
.wirelength() << " bytes long, whereas the maximum is 222 bytes." << endl
;
274 vector
<DNSBackend::KeyData
> dbkeyset
;
275 B
.getDomainKeys(zone
, dbkeyset
);
277 for(DNSBackend::KeyData
& kd
: dbkeyset
) {
278 DNSKEYRecordContent dkrc
;
279 shared_ptr
<DNSCryptoKeyEngine
>(DNSCryptoKeyEngine::makeFromISCString(dkrc
, kd
.content
));
281 if(dkrc
.d_algorithm
== DNSSECKeeper::RSASHA1
) {
282 cout
<<"[Error] zone '"<<zone
<<"' has NSEC3 semantics, but the "<< (kd
.active
? "" : "in" ) <<"active key with id "<<kd
.id
<<" has 'Algorithm: 5'. This should be corrected to 'Algorithm: 7' in the database (or NSEC3 should be disabled)."<<endl
;
290 cout
<<"[Error] zone '" << zone
<< "' has at least one invalid DNS Private Key." << endl
;
291 for (const auto &msg
: checkKeyErrors
) {
292 cout
<<"\t"<<msg
<<endl
;
296 // Check for delegation in parent zone
297 DNSName
parent(zone
);
298 while(parent
.chopOff()) {
300 if(B
.getSOAUncached(parent
, sd_p
)) {
303 B
.lookup(QType(QType::ANY
), zone
, sd_p
.domain_id
);
305 ns
|= (zr
.dr
.d_type
== QType::NS
);
307 cout
<<"[Error] No delegation for zone '"<<zone
<<"' in parent '"<<parent
<<"'"<<endl
;
315 bool hasNsAtApex
= false;
316 set
<DNSName
> tlsas
, cnames
, noncnames
, glue
, checkglue
;
317 set
<pair
<DNSName
, QType
> > checkOcclusion
;
318 set
<string
> recordcontents
;
319 map
<string
, unsigned int> ttl
;
321 ostringstream content
;
322 pair
<map
<string
, unsigned int>::iterator
,bool> ret
;
324 vector
<DNSResourceRecord
> records
;
325 if(!suppliedrecords
) {
326 DNSResourceRecord drr
;
327 sd
.db
->list(zone
, sd
.domain_id
, g_verbose
);
328 while(sd
.db
->get(drr
)) {
329 records
.push_back(drr
);
333 records
=*suppliedrecords
;
335 for(auto &rr
: records
) { // we modify this
336 if(rr
.qtype
.getCode() == QType::TLSA
)
337 tlsas
.insert(rr
.qname
);
338 if(rr
.qtype
.getCode() == QType::SOA
) {
340 stringtok(parts
, rr
.content
);
342 if(parts
.size() < 7) {
343 cout
<<"[Warning] SOA autocomplete is deprecated, missing field(s) in SOA content: "<<rr
.qname
<<" IN " <<rr
.qtype
.getName()<< " '" << rr
.content
<<"'"<<endl
;
348 for(int pleft
=parts
.size(); pleft
< 7; ++pleft
) {
354 if(rr
.qtype
.getCode() == QType::TXT
&& !rr
.content
.empty() && rr
.content
[0]!='"')
355 rr
.content
= "\""+rr
.content
+"\"";
358 shared_ptr
<DNSRecordContent
> drc(DNSRecordContent::mastermake(rr
.qtype
.getCode(), 1, rr
.content
));
359 string tmp
=drc
->serialize(rr
.qname
);
360 tmp
= drc
->getZoneRepresentation(true);
361 if (rr
.qtype
.getCode() != QType::AAAA
) {
362 if (!pdns_iequals(tmp
, rr
.content
)) {
363 if(rr
.qtype
.getCode() == QType::SOA
) {
364 tmp
= drc
->getZoneRepresentation(false);
366 if(!pdns_iequals(tmp
, rr
.content
)) {
367 cout
<<"[Warning] Parsed and original record content are not equal: "<<rr
.qname
<<" IN " <<rr
.qtype
.getName()<< " '" << rr
.content
<<"' (Content parsed as '"<<tmp
<<"')"<<endl
;
372 struct in6_addr tmpbuf
;
373 if (inet_pton(AF_INET6
, rr
.content
.c_str(), &tmpbuf
) != 1 || rr
.content
.find('.') != string::npos
) {
374 cout
<<"[Warning] Following record is not a valid IPv6 address: "<<rr
.qname
<<" IN " <<rr
.qtype
.getName()<< " '" << rr
.content
<<"'"<<endl
;
379 catch(std::exception
& e
)
381 cout
<<"[Error] Following record had a problem: \""<<rr
.qname
<<" IN "<<rr
.qtype
.getName()<<" "<<rr
.content
<<"\""<<endl
;
382 cout
<<"[Error] Error was: "<<e
.what()<<endl
;
387 if(!rr
.qname
.isPartOf(zone
)) {
388 cout
<<"[Error] Record '"<<rr
.qname
<<" IN "<<rr
.qtype
.getName()<<" "<<rr
.content
<<"' in zone '"<<zone
<<"' is out-of-zone."<<endl
;
394 content
<<rr
.qname
<<" "<<rr
.qtype
.getName()<<" "<<rr
.content
;
395 string contentstr
= content
.str();
396 if (rr
.qtype
.getCode() != QType::TXT
) {
397 contentstr
=toLower(contentstr
);
399 if (recordcontents
.count(contentstr
)) {
400 cout
<<"[Error] Duplicate record found in rrset: '"<<rr
.qname
<<" IN "<<rr
.qtype
.getName()<<" "<<rr
.content
<<"'"<<endl
;
404 recordcontents
.insert(contentstr
);
407 content
<<rr
.qname
<<" "<<rr
.qtype
.getName();
408 if (rr
.qtype
.getCode() == QType::RRSIG
) {
409 RRSIGRecordContent
rrc(rr
.content
);
410 content
<<" ("<<DNSRecordContent::NumberToType(rrc
.d_type
)<<")";
412 ret
= ttl
.insert(pair
<string
, unsigned int>(toLower(content
.str()), rr
.ttl
));
413 if (ret
.second
== false && ret
.first
->second
!= rr
.ttl
) {
414 cout
<<"[Error] TTL mismatch in rrset: '"<<rr
.qname
<<" IN " <<rr
.qtype
.getName()<<" "<<rr
.content
<<"' ("<<ret
.first
->second
<<" != "<<rr
.ttl
<<")"<<endl
;
419 if (isSecure
&& isOptOut
&& (rr
.qname
.countLabels() && rr
.qname
.getRawLabels()[0] == "*")) {
420 cout
<<"[Warning] wildcard record '"<<rr
.qname
<<" IN " <<rr
.qtype
.getName()<<" "<<rr
.content
<<"' is insecure"<<endl
;
421 cout
<<"[Info] Wildcard records in opt-out zones are insecure. Disable the opt-out flag for this zone to avoid this warning. Command: pdnsutil set-nsec3 "<<zone
<<endl
;
426 if (rr
.qtype
.getCode() == QType::NS
) {
428 } else if (rr
.qtype
.getCode() == QType::DS
) {
429 cout
<<"[Warning] DS at apex in zone '"<<zone
<<"', should not be here."<<endl
;
433 if (rr
.qtype
.getCode() == QType::SOA
) {
434 cout
<<"[Error] SOA record not at apex '"<<rr
.qname
<<" IN "<<rr
.qtype
.getName()<<" "<<rr
.content
<<"' in zone '"<<zone
<<"'"<<endl
;
437 } else if (rr
.qtype
.getCode() == QType::DNSKEY
) {
438 cout
<<"[Warning] DNSKEY record not at apex '"<<rr
.qname
<<" IN "<<rr
.qtype
.getName()<<" "<<rr
.content
<<"' in zone '"<<zone
<<"', should not be here."<<endl
;
440 } else if (rr
.qtype
.getCode() == QType::NS
) {
441 if (DNSName(rr
.content
).isPartOf(rr
.qname
)) {
442 checkglue
.insert(DNSName(toLower(rr
.content
)));
444 checkOcclusion
.insert({rr
.qname
, rr
.qtype
});
445 } else if (rr
.qtype
.getCode() == QType::A
|| rr
.qtype
.getCode() == QType::AAAA
) {
446 glue
.insert(rr
.qname
);
447 } else if (rr
.qtype
== QType::DNAME
) {
448 checkOcclusion
.insert({rr
.qname
, rr
.qtype
});
451 if((rr
.qtype
.getCode() == QType::A
|| rr
.qtype
.getCode() == QType::AAAA
) && !rr
.qname
.isWildcard() && !rr
.qname
.isHostname())
452 cout
<<"[Info] "<<rr
.qname
.toString()<<" record for '"<<rr
.qtype
.getName()<<"' is not a valid hostname."<<endl
;
454 // Check if the DNSNames that should be hostnames, are hostnames
456 checkHostnameCorrectness(rr
);
457 } catch (const std::exception
& e
) {
458 cout
<< "[Warning] " << rr
.qtype
.getName() << " record in zone '" << zone
<< ": " << e
.what() << endl
;
462 if (rr
.qtype
.getCode() == QType::CNAME
) {
463 if (!cnames
.count(rr
.qname
))
464 cnames
.insert(rr
.qname
);
466 cout
<<"[Error] Duplicate CNAME found at '"<<rr
.qname
<<"'"<<endl
;
471 if (rr
.qtype
.getCode() == QType::RRSIG
) {
473 cout
<<"[Error] RRSIG found at '"<<rr
.qname
<<"' in non-presigned zone. These do not belong in the database."<<endl
;
478 noncnames
.insert(rr
.qname
);
481 if(rr
.qtype
.getCode() == QType::NSEC
|| rr
.qtype
.getCode() == QType::NSEC3
)
483 cout
<<"[Error] NSEC or NSEC3 found at '"<<rr
.qname
<<"'. These do not belong in the database."<<endl
;
488 if(!presigned
&& rr
.qtype
.getCode() == QType::DNSKEY
)
490 if(::arg().mustDo("direct-dnskey"))
492 if(rr
.ttl
!= sd
.minimum
)
494 cout
<<"[Warning] DNSKEY TTL of "<<rr
.ttl
<<" at '"<<rr
.qname
<<"' differs from SOA minimum of "<<sd
.minimum
<<endl
;
500 cout
<<"[Warning] DNSKEY at '"<<rr
.qname
<<"' in non-presigned zone will mostly be ignored and can cause problems."<<endl
;
506 for(auto &i
: cnames
) {
507 if (noncnames
.find(i
) != noncnames
.end()) {
508 cout
<<"[Error] CNAME "<<i
<<" found, but other records with same label exist."<<endl
;
513 for(const auto &i
: tlsas
) {
514 DNSName name
= DNSName(i
);
515 name
.trimToLabels(name
.countLabels()-2);
516 if (cnames
.find(name
) == cnames
.end() && noncnames
.find(name
) == noncnames
.end()) {
517 // No specific record for the name in the TLSA record exists, this
518 // is already worth emitting a warning. Let's see if a wildcard exist.
520 DNSName
wcname(name
);
522 wcname
.prependRawLabel("*");
523 if (cnames
.find(wcname
) != cnames
.end() || noncnames
.find(wcname
) != noncnames
.end()) {
524 cout
<<"A wildcard record exist for '"<<wcname
<<"' and a TLSA record for '"<<i
<<"'.";
526 cout
<<"No record for '"<<name
<<"' exists, but a TLSA record for '"<<i
<<"' does.";
529 cout
<<" A query for '"<<name
<<"' will yield an empty response. This is most likely a mistake, please create records for '"<<name
<<"'."<<endl
;
534 cout
<<"[Error] No NS record at zone apex in zone '"<<zone
<<"'"<<endl
;
538 for(const auto &qname
: checkglue
) {
539 if (!glue
.count(qname
)) {
540 cout
<<"[Warning] Missing glue for '"<<qname
<<"' in zone '"<<zone
<<"'"<<endl
;
545 for( const auto &qname
: checkOcclusion
) {
546 for( const auto &rr
: records
) {
547 if( qname
.first
== rr
.qname
&& ((( rr
.qtype
== QType::NS
|| rr
.qtype
== QType::DS
) && qname
.second
== QType::NS
) || ( rr
.qtype
== QType::DNAME
&& qname
.second
== QType::DNAME
) ) ) {
550 if( rr
.qname
.isPartOf( qname
.first
) ) {
551 if( qname
.second
== QType::DNAME
|| ( rr
.qtype
!= QType::ENT
&& rr
.qtype
.getCode() != QType::A
&& rr
.qtype
.getCode() != QType::AAAA
) ) {
552 cout
<< "[Warning] '" << rr
.qname
<< "|" << rr
.qtype
.getName() << "' in zone '" << zone
<< "' is occluded by a ";
553 if( qname
.second
== QType::NS
) {
554 cout
<< "delegation";
558 cout
<< " at '" << qname
.first
<< "'" << endl
;
565 bool ok
, ds_ns
, done
;
566 for( const auto &rr
: records
) {
567 ok
= ( rr
.auth
== 1 );
569 done
= (suppliedrecords
|| !sd
.db
->doesDNSSEC());
570 for( const auto &qname
: checkOcclusion
) {
571 if( qname
.second
== QType::NS
) {
572 if( qname
.first
== rr
.qname
) {
579 if( rr
.qname
.isPartOf( qname
.first
) && ( qname
.first
!= rr
.qname
|| rr
.qtype
!= QType::DS
) ) {
582 if( rr
.qtype
== QType::ENT
&& qname
.first
.isPartOf( rr
.qname
) ) {
585 } else if( rr
.qname
.isPartOf( qname
.first
) && ( ( qname
.first
!= rr
.qname
|| rr
.qtype
!= QType::DS
) || rr
.qtype
== QType::NS
) ) {
591 if( ! ds_ns
&& rr
.qtype
.getCode() == QType::DS
&& rr
.qname
!= zone
) {
592 cout
<< "[Warning] DS record without a delegation '" << rr
.qname
<<"'." << endl
;
595 if( ! ok
&& ! suppliedrecords
) {
596 cout
<< "[Error] Following record is auth=" << rr
.auth
<< ", run pdnsutil rectify-zone?: " << rr
.qname
<< " IN " << rr
.qtype
.getName() << " " << rr
.content
<< endl
;
601 cout
<<"Checked "<<records
.size()<<" records of '"<<zone
<<"', "<<numerrors
<<" errors, "<<numwarnings
<<" warnings."<<endl
;
607 static int checkAllZones(DNSSECKeeper
&dk
, bool exitOnError
)
609 UeberBackend
B("default");
610 vector
<DomainInfo
> domainInfo
;
611 multi_index_container
<
614 ordered_non_unique
< member
<DomainInfo
,DNSName
,&DomainInfo::zone
>, CanonDNSNameCompare
>,
615 ordered_non_unique
< member
<DomainInfo
,uint32_t,&DomainInfo::id
> >
618 auto& seenNames
= seenInfos
.get
<0>();
619 auto& seenIds
= seenInfos
.get
<1>();
621 B
.getAllDomains(&domainInfo
, true);
623 for(auto di
: domainInfo
) {
624 if (checkZone(dk
, B
, di
.zone
) > 0) {
628 auto seenName
= seenNames
.find(di
.zone
);
629 if (seenName
!= seenNames
.end()) {
630 cout
<<"[Error] Another SOA for zone '"<<di
.zone
<<"' (serial "<<di
.serial
<<") has already been seen (serial "<<seenName
->serial
<<")."<<endl
;
634 auto seenId
= seenIds
.find(di
.id
);
635 if (seenId
!= seenIds
.end()) {
636 cout
<<"[Error] Domain ID "<<di
.id
<<" of '"<<di
.zone
<<"' in backend "<<di
.backend
->getPrefix()<<" has already been used by zone '"<<seenId
->zone
<<"' in backend "<<seenId
->backend
->getPrefix()<<"."<<endl
;
640 seenInfos
.insert(di
);
642 if(errors
&& exitOnError
)
645 cout
<<"Checked "<<domainInfo
.size()<<" zones, "<<errors
<<" had errors."<<endl
;
651 static int increaseSerial(const DNSName
& zone
, DNSSECKeeper
&dk
)
653 UeberBackend
B("default");
655 if(!B
.getSOAUncached(zone
, sd
)) {
656 cerr
<<"No SOA for zone '"<<zone
<<"'"<<endl
;
660 if (dk
.isPresigned(zone
)) {
661 cerr
<<"Serial increase of presigned zone '"<<zone
<<"' is not allowed."<<endl
;
666 dk
.getSoaEdit(zone
, soaEditKind
);
668 DNSResourceRecord rr
;
669 makeIncreasedSOARecord(sd
, "SOA-EDIT-INCREASE", soaEditKind
, rr
);
671 sd
.db
->startTransaction(zone
, -1);
673 if (!sd
.db
->replaceRRSet(sd
.domain_id
, zone
, rr
.qtype
, vector
<DNSResourceRecord
>(1, rr
))) {
674 sd
.db
->abortTransaction();
675 cerr
<<"Backend did not replace SOA record. Backend might not support this operation."<<endl
;
679 if (sd
.db
->doesDNSSEC()) {
680 NSEC3PARAMRecordContent ns3pr
;
682 bool haveNSEC3
=dk
.getNSEC3PARAM(zone
, &ns3pr
, &narrow
);
687 ordername
=DNSName(toBase32Hex(hashQNameWithSalt(ns3pr
, zone
)));
689 ordername
=DNSName("");
691 cerr
<<"'"<<rr
.qname
<<"' -> '"<< ordername
<<"'"<<endl
;
692 sd
.db
->updateDNSSECOrderNameAndAuth(sd
.domain_id
, rr
.qname
, ordername
, true);
695 sd
.db
->commitTransaction();
697 cout
<<"SOA serial for zone "<<zone
<<" set to "<<sd
.serial
<<endl
;
701 static int deleteZone(const DNSName
&zone
) {
704 if (! B
.getDomainInfo(zone
, di
)) {
705 cerr
<<"Domain '"<<zone
<<"' not found!"<<endl
;
709 if(di
.backend
->deleteDomain(zone
))
712 cerr
<<"Failed to delete domain '"<<zone
<<"'"<<endl
;;
716 static void listKey(DomainInfo
const &di
, DNSSECKeeper
& dk
, bool printHeader
= true) {
718 cout
<<"Zone Type Size Algorithm ID Location Keytag"<<endl
;
719 cout
<<"----------------------------------------------------------------------------------"<<endl
;
721 unsigned int spacelen
= 0;
722 for (auto const &key
: dk
.getKeys(di
.zone
)) {
724 if (di
.zone
.toStringNoDot().length() > 29)
725 cout
<<endl
<<string(30, ' ');
727 cout
<<string(30 - di
.zone
.toStringNoDot().length(), ' ');
729 cout
<<DNSSECKeeper::keyTypeToString(key
.second
.keyType
)<<" ";
731 spacelen
= (std::to_string(key
.first
.getKey()->getBits()).length() >= 8) ? 1 : 8 - std::to_string(key
.first
.getKey()->getBits()).length();
732 if (key
.first
.getKey()->getBits() < 1) {
733 cout
<<"invalid "<<endl
;
736 cout
<<key
.first
.getKey()->getBits()<<string(spacelen
, ' ');
739 string algname
= DNSSECKeeper::algorithm2name(key
.first
.d_algorithm
);
740 spacelen
= (algname
.length() >= 13) ? 1 : 13 - algname
.length();
741 cout
<<algname
<<string(spacelen
, ' ');
743 spacelen
= (std::to_string(key
.second
.id
).length() > 5) ? 1 : 5 - std::to_string(key
.second
.id
).length();
744 cout
<<key
.second
.id
<<string(spacelen
, ' ');
747 auto stormap
= key
.first
.getKey()->convertToISCVector();
748 string engine
, slot
, label
= "";
749 for (auto const &elem
: stormap
) {
750 //cout<<elem.first<<" "<<elem.second<<endl;
751 if (elem
.first
== "Engine")
752 engine
= elem
.second
;
753 if (elem
.first
== "Slot")
755 if (elem
.first
== "Label")
758 if (engine
.empty() || slot
.empty()){
761 spacelen
= (engine
.length()+slot
.length()+label
.length()+2 >= 12) ? 1 : 12 - engine
.length()-slot
.length()-label
.length()-2;
762 cout
<<engine
<<","<<slot
<<","<<label
<<string(spacelen
, ' ');
767 cout
<<key
.first
.getDNSKEY().getTag()<<endl
;
771 static int listKeys(const string
&zname
, DNSSECKeeper
& dk
){
772 UeberBackend
B("default");
774 if (zname
!= "all") {
776 if(!B
.getDomainInfo(DNSName(zname
), di
)) {
777 cerr
<< "Zone "<<zname
<<" not found."<<endl
;
782 vector
<DomainInfo
> domainInfo
;
783 B
.getAllDomains(&domainInfo
);
784 bool printHeader
= true;
785 for (const auto& di
: domainInfo
) {
786 listKey(di
, dk
, printHeader
);
793 static int listZone(const DNSName
&zone
) {
797 if (! B
.getDomainInfo(zone
, di
)) {
798 cerr
<<"Domain '"<<zone
<<"' not found!"<<endl
;
801 di
.backend
->list(zone
, di
.id
);
802 DNSResourceRecord rr
;
803 cout
<<"$ORIGIN ."<<endl
;
804 cout
.sync_with_stdio(false);
806 while(di
.backend
->get(rr
)) {
807 if(rr
.qtype
.getCode()) {
808 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] != '.')
809 rr
.content
.append(1, '.');
811 cout
<<rr
.qname
<<"\t"<<rr
.ttl
<<"\tIN\t"<<rr
.qtype
.getName()<<"\t"<<rr
.content
<<"\n";
818 // lovingly copied from http://stackoverflow.com/questions/1798511/how-to-avoid-press-enter-with-any-getchar
819 static int read1char(){
821 static struct termios oldt
, newt
;
823 /*tcgetattr gets the parameters of the current terminal
824 STDIN_FILENO will tell tcgetattr that it should write the settings
826 tcgetattr( STDIN_FILENO
, &oldt
);
827 /*now the settings will be copied*/
830 /*ICANON normally takes care that one line at a time will be processed
831 that means it will return if it sees a "\n" or an EOF or an EOL*/
832 newt
.c_lflag
&= ~(ICANON
);
834 /*Those new settings will be set to STDIN
835 TCSANOW tells tcsetattr to change attributes immediately. */
836 tcsetattr( STDIN_FILENO
, TCSANOW
, &newt
);
840 /*restore the old settings*/
841 tcsetattr( STDIN_FILENO
, TCSANOW
, &oldt
);
846 static int clearZone(DNSSECKeeper
& dk
, const DNSName
&zone
) {
850 if (! B
.getDomainInfo(zone
, di
)) {
851 cerr
<<"Domain '"<<zone
<<"' not found!"<<endl
;
854 if(!di
.backend
->startTransaction(zone
, di
.id
)) {
855 cerr
<<"Unable to start transaction for load of zone '"<<zone
<<"'"<<endl
;
858 di
.backend
->commitTransaction();
862 static int editZone(const DNSName
&zone
) {
867 if (! B
.getDomainInfo(zone
, di
)) {
868 cerr
<<"Domain '"<<zone
<<"' not found!"<<endl
;
871 vector
<DNSRecord
> pre
, post
;
872 char tmpnam
[]="/tmp/pdnsutil-XXXXXX";
873 int tmpfd
=mkstemp(tmpnam
);
875 unixDie("Making temporary filename in "+string(tmpnam
));
877 ~deleteme() { unlink(d_name
.c_str()); }
878 deleteme(string name
) : d_name(name
) {}
882 vector
<DNSResourceRecord
> checkrr
;
884 string editor
="editor";
885 if(auto e
=getenv("EDITOR")) // <3
889 di
.backend
->list(zone
, di
.id
);
890 pre
.clear(); post
.clear();
892 if(tmpfd
< 0 && (tmpfd
=open(tmpnam
, O_CREAT
| O_WRONLY
| O_TRUNC
, 0600)) < 0)
893 unixDie("Error reopening temporary file "+string(tmpnam
));
894 string
header("; Warning - every name in this file is ABSOLUTE!\n$ORIGIN .\n");
895 if(write(tmpfd
, header
.c_str(), header
.length()) < 0)
896 unixDie("Writing zone to temporary file");
897 DNSResourceRecord rr
;
898 while(di
.backend
->get(rr
)) {
899 if(!rr
.qtype
.getCode())
904 sort(pre
.begin(), pre
.end(), DNSRecord::prettyCompare
);
905 for(const auto& dr
: pre
) {
907 os
<<dr
.d_name
<<"\t"<<dr
.d_ttl
<<"\tIN\t"<<DNSRecordContent::NumberToType(dr
.d_type
)<<"\t"<<dr
.d_content
->getZoneRepresentation(true)<<endl
;
908 if(write(tmpfd
, os
.str().c_str(), os
.str().length()) < 0)
909 unixDie("Writing zone to temporary file");
917 cmdline
+="+"+std::to_string(gotoline
)+" ";
919 int err
=system(cmdline
.c_str());
921 unixDie("Editing file with: '"+cmdline
+"', perhaps set EDITOR variable");
924 ZoneParserTNG
zpt(tmpnam
, g_rootdnsname
);
925 zpt
.setMaxGenerateSteps(::arg().asNum("max-generate-steps"));
926 DNSResourceRecord zrr
;
927 map
<pair
<DNSName
,uint16_t>, vector
<DNSRecord
> > grouped
;
929 while(zpt
.get(zrr
)) {
932 grouped
[{dr
.d_name
,dr
.d_type
}].push_back(dr
);
935 catch(std::exception
& e
) {
936 cerr
<<"Problem: "<<e
.what()<<" "<<zpt
.getLineOfFile()<<endl
;
937 auto fnum
= zpt
.getLineNumAndFile();
938 gotoline
= fnum
.second
;
942 sort(post
.begin(), post
.end(), DNSRecord::prettyCompare
);
945 for(const DNSRecord
& rr
: post
) {
946 DNSResourceRecord drr
= DNSResourceRecord::fromWire(rr
);
947 drr
.domain_id
= di
.id
;
948 checkrr
.push_back(drr
);
950 if(checkZone(dk
, B
, zone
, &checkrr
)) {
952 cerr
<<"\x1b[31;1mThere was a problem with your zone\x1b[0m\nOptions are: (e)dit your changes, (r)etry with original zone, (a)pply change anyhow, (q)uit: "<<endl
;
968 vector
<DNSRecord
> diff
;
970 map
<pair
<DNSName
,uint16_t>, string
> changed
;
971 set_difference(pre
.cbegin(), pre
.cend(), post
.cbegin(), post
.cend(), back_inserter(diff
), DNSRecord::prettyCompare
);
972 for(const auto& d
: diff
) {
974 str
<<"\033[0;31m-"<< d
.d_name
<<" "<<d
.d_ttl
<<" IN "<<DNSRecordContent::NumberToType(d
.d_type
)<<" "<<d
.d_content
->getZoneRepresentation(true)<<"\033[0m"<<endl
;
975 changed
[{d
.d_name
,d
.d_type
}] += str
.str();
979 set_difference(post
.cbegin(), post
.cend(), pre
.cbegin(), pre
.cend(), back_inserter(diff
), DNSRecord::prettyCompare
);
980 for(const auto& d
: diff
) {
983 str
<<"\033[0;32m+"<< d
.d_name
<<" "<<d
.d_ttl
<<" IN "<<DNSRecordContent::NumberToType(d
.d_type
)<<" "<<d
.d_content
->getZoneRepresentation(true)<<"\033[0m"<<endl
;
984 changed
[{d
.d_name
,d
.d_type
}]+=str
.str();
986 cout
<<"Detected the following changes:"<<endl
;
987 for(const auto& c
: changed
) {
990 if (changed
.size() > 0) {
991 if (changed
.find({zone
, QType::SOA
}) == changed
.end()) {
992 cout
<<endl
<<"You have not updated the SOA record! Would you like to increase-serial?"<<endl
;
993 cout
<<"(y)es - increase serial, (n)o - leave SOA record as is, (e)dit your changes, (q)uit:"<<endl
;
998 DNSRecord oldSoaDR
= grouped
[{zone
, QType::SOA
}].at(0); // there should be only one SOA record, so we can use .at(0);
1000 str
<<"\033[0;31m-"<< oldSoaDR
.d_name
<<" "<<oldSoaDR
.d_ttl
<<" IN "<<DNSRecordContent::NumberToType(oldSoaDR
.d_type
)<<" "<<oldSoaDR
.d_content
->getZoneRepresentation(true)<<"\033[0m"<<endl
;
1003 B
.getSOAUncached(zone
, sd
);
1004 // TODO: do we need to check for presigned? here or maybe even all the way before edit-zone starts?
1007 dk
.getSoaEdit(zone
, soaEditKind
);
1009 DNSResourceRecord rr
;
1010 makeIncreasedSOARecord(sd
, "SOA-EDIT-INCREASE", soaEditKind
, rr
);
1012 str
<<"\033[0;32m+"<< dr
.d_name
<<" "<<dr
.d_ttl
<<" IN "<<DNSRecordContent::NumberToType(dr
.d_type
)<<" "<<dr
.d_content
->getZoneRepresentation(true)<<"\033[0m"<<endl
;
1014 changed
[{dr
.d_name
, dr
.d_type
}]+=str
.str();
1015 grouped
[{dr
.d_name
, dr
.d_type
}].at(0) = dr
;
1019 return EXIT_FAILURE
;
1032 if(changed
.empty()) {
1033 cout
<<endl
<<"No changes to apply."<<endl
;
1034 return(EXIT_SUCCESS
);
1036 cout
<<endl
<<"(a)pply these changes, (e)dit again, (r)etry with original zone, (q)uit: ";
1041 return(EXIT_SUCCESS
);
1046 else if(changed
.empty() || c
!='a')
1049 di
.backend
->startTransaction(zone
, -1);
1050 for(const auto& change
: changed
) {
1051 vector
<DNSResourceRecord
> vrr
;
1052 for(const DNSRecord
& rr
: grouped
[change
.first
]) {
1053 DNSResourceRecord crr
= DNSResourceRecord::fromWire(rr
);
1054 crr
.domain_id
= di
.id
;
1057 di
.backend
->replaceRRSet(di
.id
, change
.first
.first
, QType(change
.first
.second
), vrr
);
1059 rectifyZone(dk
, zone
, false, false);
1060 di
.backend
->commitTransaction();
1061 return EXIT_SUCCESS
;
1064 static int xcryptIP(const std::string
& cmd
, const std::string
& ip
, const std::string
& rkey
)
1067 ComboAddress
ca(ip
), ret
;
1069 if(cmd
=="ipencrypt")
1070 ret
= encryptCA(ca
, rkey
);
1072 ret
= decryptCA(ca
, rkey
);
1074 cout
<<ret
.toString()<<endl
;
1075 return EXIT_SUCCESS
;
1079 static int loadZone(DNSName zone
, const string
& fname
) {
1083 if (B
.getDomainInfo(zone
, di
)) {
1084 cerr
<<"Domain '"<<zone
<<"' exists already, replacing contents"<<endl
;
1087 cerr
<<"Creating '"<<zone
<<"'"<<endl
;
1088 B
.createDomain(zone
);
1090 if(!B
.getDomainInfo(zone
, di
)) {
1091 cerr
<<"Domain '"<<zone
<<"' was not created - perhaps backend ("<<::arg()["launch"]<<") does not support storing new zones."<<endl
;
1092 return EXIT_FAILURE
;
1095 DNSBackend
* db
= di
.backend
;
1096 ZoneParserTNG
zpt(fname
, zone
);
1097 zpt
.setMaxGenerateSteps(::arg().asNum("max-generate-steps"));
1099 DNSResourceRecord rr
;
1100 if(!db
->startTransaction(zone
, di
.id
)) {
1101 cerr
<<"Unable to start transaction for load of zone '"<<zone
<<"'"<<endl
;
1102 return EXIT_FAILURE
;
1105 bool haveSOA
= false;
1106 while(zpt
.get(rr
)) {
1107 if(!rr
.qname
.isPartOf(zone
) && rr
.qname
!=zone
) {
1108 cerr
<<"File contains record named '"<<rr
.qname
<<"' which is not part of zone '"<<zone
<<"'"<<endl
;
1109 return EXIT_FAILURE
;
1111 if (rr
.qtype
== QType::SOA
) {
1117 db
->feedRecord(rr
, DNSName());
1119 db
->commitTransaction();
1120 return EXIT_SUCCESS
;
1123 static int createZone(const DNSName
&zone
, const DNSName
& nsname
) {
1126 if (B
.getDomainInfo(zone
, di
)) {
1127 cerr
<<"Domain '"<<zone
<<"' exists already"<<endl
;
1128 return EXIT_FAILURE
;
1130 cerr
<<"Creating empty zone '"<<zone
<<"'"<<endl
;
1131 B
.createDomain(zone
);
1132 if(!B
.getDomainInfo(zone
, di
)) {
1133 cerr
<<"Domain '"<<zone
<<"' was not created!"<<endl
;
1134 return EXIT_FAILURE
;
1137 DNSResourceRecord rr
;
1140 rr
.ttl
= ::arg().asNum("default-ttl");
1143 string soa
= (boost::format("%s %s 1")
1144 % (nsname
.empty() ? ::arg()["default-soa-name"] : nsname
.toString())
1145 % (::arg().isEmpty("default-soa-mail") ? (DNSName("hostmaster.") + zone
).toString() : ::arg()["default-soa-mail"])
1148 fillSOAData(soa
, sd
); // fills out default values for us
1149 rr
.content
= makeSOAContent(sd
)->getZoneRepresentation(true);
1150 rr
.domain_id
= di
.id
;
1151 di
.backend
->startTransaction(zone
, di
.id
);
1152 di
.backend
->feedRecord(rr
, DNSName());
1153 if(!nsname
.empty()) {
1154 cout
<<"Also adding one NS record"<<endl
;
1156 rr
.content
=nsname
.toStringNoDot();
1157 di
.backend
->feedRecord(rr
, DNSName());
1160 di
.backend
->commitTransaction();
1162 return EXIT_SUCCESS
;
1165 static int createSlaveZone(const vector
<string
>& cmds
) {
1168 DNSName
zone(cmds
[1]);
1169 if (B
.getDomainInfo(zone
, di
)) {
1170 cerr
<<"Domain '"<<zone
<<"' exists already"<<endl
;
1171 return EXIT_FAILURE
;
1173 vector
<string
> masters
;
1174 for (unsigned i
=2; i
< cmds
.size(); i
++) {
1175 ComboAddress
master(cmds
[i
], 53);
1176 masters
.push_back(master
.toStringWithPort());
1178 cerr
<<"Creating slave zone '"<<zone
<<"', with master(s) '"<<boost::join(masters
, ",")<<"'"<<endl
;
1179 B
.createDomain(zone
);
1180 if(!B
.getDomainInfo(zone
, di
)) {
1181 cerr
<<"Domain '"<<zone
<<"' was not created!"<<endl
;
1182 return EXIT_FAILURE
;
1184 di
.backend
->setKind(zone
, DomainInfo::Slave
);
1185 di
.backend
->setMaster(zone
, boost::join(masters
, ","));
1186 return EXIT_SUCCESS
;
1189 static int changeSlaveZoneMaster(const vector
<string
>& cmds
) {
1192 DNSName
zone(cmds
[1]);
1193 if (!B
.getDomainInfo(zone
, di
)) {
1194 cerr
<<"Domain '"<<zone
<<"' doesn't exist"<<endl
;
1195 return EXIT_FAILURE
;
1197 vector
<string
> masters
;
1198 for (unsigned i
=2; i
< cmds
.size(); i
++) {
1199 ComboAddress
master(cmds
[i
], 53);
1200 masters
.push_back(master
.toStringWithPort());
1202 cerr
<<"Updating slave zone '"<<zone
<<"', master(s) to '"<<boost::join(masters
, ",")<<"'"<<endl
;
1204 di
.backend
->setMaster(zone
, boost::join(masters
, ","));
1205 return EXIT_SUCCESS
;
1207 catch (PDNSException
& e
) {
1208 cerr
<<"Setting master for zone '"<<zone
<<"' failed: "<<e
.reason
<<endl
;
1209 return EXIT_FAILURE
;
1213 // add-record ZONE name type [ttl] "content" ["content"]
1214 static int addOrReplaceRecord(bool addOrReplace
, const vector
<string
>& cmds
) {
1215 DNSResourceRecord rr
;
1216 vector
<DNSResourceRecord
> newrrs
;
1217 DNSName
zone(cmds
[1]);
1222 name
=DNSName(cmds
[2])+zone
;
1224 rr
.qtype
= DNSRecordContent::TypeToNumber(cmds
[3]);
1225 rr
.ttl
= ::arg().asNum("default-ttl");
1230 if(!B
.getDomainInfo(zone
, di
)) {
1231 cerr
<<"Domain '"<<zone
<<"' does not exist"<<endl
;
1232 return EXIT_FAILURE
;
1235 rr
.domain_id
= di
.id
;
1237 DNSResourceRecord oldrr
;
1239 di
.backend
->startTransaction(zone
, -1);
1241 if(addOrReplace
) { // the 'add' case
1242 di
.backend
->lookup(rr
.qtype
, rr
.qname
, di
.id
);
1244 while(di
.backend
->get(oldrr
))
1245 newrrs
.push_back(oldrr
);
1248 unsigned int contentStart
= 4;
1249 if(cmds
.size() > 5) {
1250 rr
.ttl
=atoi(cmds
[4].c_str());
1251 if(std::to_string(rr
.ttl
)==cmds
[4]) {
1255 rr
.ttl
= ::arg().asNum("default-ttl");
1259 di
.backend
->lookup(QType(QType::ANY
), rr
.qname
, di
.id
);
1261 if(rr
.qtype
.getCode() == QType::CNAME
) { // this will save us SO many questions
1263 while(di
.backend
->get(oldrr
)) {
1264 if(addOrReplace
|| oldrr
.qtype
.getCode() != QType::CNAME
) // the replace case is ok if we replace one CNAME by the other
1268 cerr
<<"Attempting to add CNAME to "<<rr
.qname
<<" which already had existing records"<<endl
;
1269 return EXIT_FAILURE
;
1273 while(di
.backend
->get(oldrr
)) {
1274 if(oldrr
.qtype
.getCode() == QType::CNAME
)
1278 cerr
<<"Attempting to add record to "<<rr
.qname
<<" which already had a CNAME record"<<endl
;
1279 return EXIT_FAILURE
;
1284 cout
<<"Current records for "<<rr
.qname
<<" IN "<<rr
.qtype
.getName()<<" will be replaced"<<endl
;
1286 for(auto i
= contentStart
; i
< cmds
.size() ; ++i
) {
1287 rr
.content
= DNSRecordContent::mastermake(rr
.qtype
.getCode(), QClass::IN
, cmds
[i
])->getZoneRepresentation(true);
1289 newrrs
.push_back(rr
);
1293 di
.backend
->replaceRRSet(di
.id
, name
, rr
.qtype
, newrrs
);
1294 // need to be explicit to bypass the ueberbackend cache!
1295 di
.backend
->lookup(rr
.qtype
, name
, di
.id
);
1296 cout
<<"New rrset:"<<endl
;
1297 while(di
.backend
->get(rr
)) {
1298 cout
<<rr
.qname
.toString()<<" "<<rr
.ttl
<<" IN "<<rr
.qtype
.getName()<<" "<<rr
.content
<<endl
;
1300 di
.backend
->commitTransaction();
1301 return EXIT_SUCCESS
;
1304 // delete-rrset zone name type
1305 static int deleteRRSet(const std::string
& zone_
, const std::string
& name_
, const std::string
& type_
)
1309 DNSName
zone(zone_
);
1310 if(!B
.getDomainInfo(zone
, di
)) {
1311 cerr
<<"Domain '"<<zone
<<"' does not exist"<<endl
;
1312 return EXIT_FAILURE
;
1319 name
=DNSName(name_
)+zone
;
1321 QType
qt(QType::chartocode(type_
.c_str()));
1322 di
.backend
->startTransaction(zone
, -1);
1323 di
.backend
->replaceRRSet(di
.id
, name
, qt
, vector
<DNSResourceRecord
>());
1324 di
.backend
->commitTransaction();
1325 return EXIT_SUCCESS
;
1328 static int listAllZones(const string
&type
="") {
1330 int kindFilter
= -1;
1332 if (toUpper(type
) == "MASTER")
1334 else if (toUpper(type
) == "SLAVE")
1336 else if (toUpper(type
) == "NATIVE")
1339 cerr
<<"Syntax: pdnsutil list-all-zones [master|slave|native]"<<endl
;
1344 UeberBackend
B("default");
1346 vector
<DomainInfo
> domains
;
1347 B
.getAllDomains(&domains
, true);
1350 for (const auto& di
: domains
) {
1351 if (di
.kind
== kindFilter
|| kindFilter
== -1) {
1352 cout
<<di
.zone
<<endl
;
1358 if (kindFilter
!= -1)
1359 cout
<<type
<<" zonecount: "<<count
<<endl
;
1361 cout
<<"All zonecount: "<<count
<<endl
;
1367 static bool testAlgorithm(int algo
)
1369 return DNSCryptoKeyEngine::testOne(algo
);
1372 static bool testAlgorithms()
1374 return DNSCryptoKeyEngine::testAll();
1377 static void testSpeed(DNSSECKeeper
& dk
, const DNSName
& zone
, const string
& remote
, int cores
)
1379 DNSResourceRecord rr
;
1380 rr
.qname
=DNSName("blah")+zone
;
1384 rr
.qclass
= QClass::IN
;
1386 UeberBackend
db("key-only");
1388 if ( ! db
.backends
.size() )
1390 throw runtime_error("No backends available for DNSSEC key storage");
1393 ChunkedSigningPipe
csp(DNSName(zone
), 1, cores
);
1395 vector
<DNSZoneRecord
> signatures
;
1397 unsigned char* octets
= (unsigned char*)&rnd
;
1401 for(unsigned int n
=0; n
< 100000; ++n
) {
1402 rnd
= dns_random(UINT32_MAX
);
1403 snprintf(tmp
, sizeof(tmp
), "%d.%d.%d.%d",
1404 octets
[0], octets
[1], octets
[2], octets
[3]);
1407 snprintf(tmp
, sizeof(tmp
), "r-%u", rnd
);
1408 rr
.qname
=DNSName(tmp
)+zone
;
1410 dzr
.dr
=DNSRecord(rr
);
1412 while(signatures
= csp
.getChunk(), !signatures
.empty())
1415 cerr
<<"Flushing the pipe, "<<csp
.d_signed
<<" signed, "<<csp
.d_queued
<<" queued, "<<csp
.d_outstanding
<<" outstanding"<< endl
;
1416 cerr
<<"Net speed: "<<csp
.d_signed
/ (dt
.udiffNoReset()/1000000.0) << " sigs/s"<<endl
;
1417 while(signatures
= csp
.getChunk(true), !signatures
.empty())
1419 cerr
<<"Done, "<<csp
.d_signed
<<" signed, "<<csp
.d_queued
<<" queued, "<<csp
.d_outstanding
<<" outstanding"<< endl
;
1420 cerr
<<"Net speed: "<<csp
.d_signed
/ (dt
.udiff()/1000000.0) << " sigs/s"<<endl
;
1423 static void verifyCrypto(const string
& zone
)
1425 ZoneParserTNG
zpt(zone
);
1426 zpt
.setMaxGenerateSteps(::arg().asNum("max-generate-steps"));
1427 DNSResourceRecord rr
;
1428 DNSKEYRecordContent drc
;
1429 RRSIGRecordContent rrc
;
1430 DSRecordContent dsrc
;
1431 sortedRecords_t toSign
;
1432 DNSName qname
, apex
;
1433 dsrc
.d_digesttype
=0;
1434 while(zpt
.get(rr
)) {
1435 if(rr
.qtype
.getCode() == QType::DNSKEY
) {
1436 cerr
<<"got DNSKEY!"<<endl
;
1438 drc
= *std::dynamic_pointer_cast
<DNSKEYRecordContent
>(DNSRecordContent::mastermake(QType::DNSKEY
, QClass::IN
, rr
.content
));
1440 else if(rr
.qtype
.getCode() == QType::RRSIG
) {
1441 cerr
<<"got RRSIG"<<endl
;
1442 rrc
= *std::dynamic_pointer_cast
<RRSIGRecordContent
>(DNSRecordContent::mastermake(QType::RRSIG
, QClass::IN
, rr
.content
));
1444 else if(rr
.qtype
.getCode() == QType::DS
) {
1445 cerr
<<"got DS"<<endl
;
1446 dsrc
= *std::dynamic_pointer_cast
<DSRecordContent
>(DNSRecordContent::mastermake(QType::DS
, QClass::IN
, rr
.content
));
1450 toSign
.insert(DNSRecordContent::mastermake(rr
.qtype
.getCode(), QClass::IN
, rr
.content
));
1454 string msg
= getMessageForRRSET(qname
, rrc
, toSign
);
1455 cerr
<<"Verify: "<<DNSCryptoKeyEngine::makeFromPublicKeyString(drc
.d_algorithm
, drc
.d_key
)->verify(msg
, rrc
.d_signature
)<<endl
;
1456 if(dsrc
.d_digesttype
) {
1457 cerr
<<"Calculated DS: "<<apex
.toString()<<" IN DS "<<makeDSFromDNSKey(apex
, drc
, dsrc
.d_digesttype
).getZoneRepresentation()<<endl
;
1458 cerr
<<"Original DS: "<<apex
.toString()<<" IN DS "<<dsrc
.getZoneRepresentation()<<endl
;
1462 static bool disableDNSSECOnZone(DNSSECKeeper
& dk
, const DNSName
& zone
)
1464 UeberBackend
B("default");
1467 if (!B
.getDomainInfo(zone
, di
)){
1468 cerr
<< "No such zone in the database" << endl
;
1473 bool ret
= dk
.unSecureZone(zone
, error
, info
);
1475 cerr
<< error
<< endl
;
1480 static int setZoneAccount(const DNSName
& zone
, const string
&account
)
1482 UeberBackend
B("default");
1485 if (!B
.getDomainInfo(zone
, di
)){
1486 cerr
<< "No such zone "<<zone
<<" in the database" << endl
;
1487 return EXIT_FAILURE
;
1489 if(!di
.backend
->setAccount(zone
, account
)) {
1490 cerr
<<"Could not find backend willing to accept new zone configuration"<<endl
;
1491 return EXIT_FAILURE
;
1493 return EXIT_SUCCESS
;
1496 static int setZoneKind(const DNSName
& zone
, const DomainInfo::DomainKind kind
)
1498 UeberBackend
B("default");
1501 if (!B
.getDomainInfo(zone
, di
)){
1502 cerr
<< "No such zone "<<zone
<<" in the database" << endl
;
1503 return EXIT_FAILURE
;
1505 if(!di
.backend
->setKind(zone
, kind
)) {
1506 cerr
<<"Could not find backend willing to accept new zone configuration"<<endl
;
1507 return EXIT_FAILURE
;
1509 return EXIT_SUCCESS
;
1512 static bool showZone(DNSSECKeeper
& dk
, const DNSName
& zone
, bool exportDS
= false)
1514 UeberBackend
B("default");
1517 if (!B
.getDomainInfo(zone
, di
)){
1518 cerr
<< "No such zone in the database" << endl
;
1522 if (!di
.account
.empty()) {
1523 cout
<<"This zone is owned by "<<di
.account
<<endl
;
1526 cout
<<"This is a "<<DomainInfo::getKindString(di
.kind
)<<" zone"<<endl
;
1527 if(di
.kind
== DomainInfo::Master
) {
1528 cout
<<"Last SOA serial number we notified: "<<di
.notified_serial
<<" ";
1530 if(B
.getSOAUncached(zone
, sd
)) {
1531 if(sd
.serial
== di
.notified_serial
)
1535 cout
<<sd
.serial
<<" (serial in the database)"<<endl
;
1538 else if(di
.kind
== DomainInfo::Slave
) {
1539 cout
<<"Master"<<addS(di
.masters
)<<": ";
1540 for(const auto& m
: di
.masters
)
1541 cout
<<m
.toStringWithPort()<<" ";
1544 localtime_r(&di
.last_check
, &tm
);
1547 strftime(buf
, sizeof(buf
)-1, "%a %F %H:%M:%S", &tm
);
1549 strncpy(buf
, "Never", sizeof(buf
)-1);
1550 buf
[sizeof(buf
)-1] = '\0';
1551 cout
<<"Last time we got update from master: "<<buf
<<endl
;
1553 if(B
.getSOAUncached(zone
, sd
)) {
1554 cout
<<"SOA serial in database: "<<sd
.serial
<<endl
;
1555 cout
<<"Refresh interval: "<<sd
.refresh
<<" seconds"<<endl
;
1558 cout
<<"No SOA serial found in database"<<endl
;
1562 if(!dk
.isSecuredZone(zone
)) {
1563 auto &outstream
= (exportDS
? cerr
: cout
);
1564 outstream
<< "Zone is not actively secured" << endl
;
1566 // it does not make sense to proceed here, and it might be useful
1567 // for scripts to know that something is odd here
1572 NSEC3PARAMRecordContent ns3pr
;
1574 bool haveNSEC3
=dk
.getNSEC3PARAM(zone
, &ns3pr
, &narrow
);
1576 DNSSECKeeper::keyset_t keyset
=dk
.getKeys(zone
);
1579 std::vector
<std::string
> meta
;
1581 if (B
.getDomainMetadata(zone
, "TSIG-ALLOW-AXFR", meta
) && meta
.size() > 0) {
1582 cout
<< "Zone has following allowed TSIG key(s): " << boost::join(meta
, ",") << endl
;
1586 if (B
.getDomainMetadata(zone
, "AXFR-MASTER-TSIG", meta
) && meta
.size() > 0) {
1587 cout
<< "Zone uses following TSIG key(s): " << boost::join(meta
, ",") << endl
;
1590 std::map
<std::string
, std::vector
<std::string
> > metamap
;
1591 if(B
.getAllDomainMetadata(zone
, metamap
)) {
1592 cout
<<"Metadata items: ";
1597 for(const auto& m
: metamap
) {
1598 for(const auto& i
: m
.second
)
1599 cout
<< '\t' << m
.first
<<'\t' << i
<<endl
;
1605 if (dk
.isPresigned(zone
)) {
1607 cout
<<"Zone is presigned"<<endl
;
1611 vector
<DNSKEYRecordContent
> keys
;
1614 di
.backend
->lookup(QType(QType::DNSKEY
), zone
, di
.id
);
1615 while(di
.backend
->get(zr
)) {
1616 keys
.push_back(*getRR
<DNSKEYRecordContent
>(zr
.dr
));
1620 cerr
<< "No keys for zone '"<<zone
<<"'."<<endl
;
1626 cout
<<"Zone has NSEC semantics"<<endl
;
1628 cout
<<"Zone has " << (narrow
? "NARROW " : "") <<"hashed NSEC3 semantics, configuration: "<<ns3pr
.getZoneRepresentation()<<endl
;
1629 cout
<< "keys: "<<endl
;
1632 sort(keys
.begin(),keys
.end());
1633 reverse(keys
.begin(),keys
.end());
1634 for(const auto& key
: keys
) {
1635 string algname
= DNSSECKeeper::algorithm2name(key
.d_algorithm
);
1639 std::shared_ptr
<DNSCryptoKeyEngine
> engine(DNSCryptoKeyEngine::makeFromPublicKeyString(key
.d_algorithm
, key
.d_key
)); // throws on unknown algo or bad key
1640 bits
=engine
->getBits();
1642 catch(std::exception
& e
) {
1643 cerr
<<"Could not process key to extract metadata: "<<e
.what()<<endl
;
1646 cout
<< (key
.d_flags
== 257 ? "KSK" : "ZSK") << ", tag = " << key
.getTag() << ", algo = "<<(int)key
.d_algorithm
<< ", bits = " << bits
<< endl
;
1647 cout
<< "DNSKEY = " <<zone
.toString()<<" IN DNSKEY "<< key
.getZoneRepresentation() << "; ( " + algname
+ " ) " <<endl
;
1650 const std::string
prefix(exportDS
? "" : "DS = ");
1651 cout
<<prefix
<<zone
.toString()<<" IN DS "<<makeDSFromDNSKey(zone
, key
, DNSSECKeeper::DIGEST_SHA1
).getZoneRepresentation() << " ; ( SHA1 digest )" << endl
;
1652 cout
<<prefix
<<zone
.toString()<<" IN DS "<<makeDSFromDNSKey(zone
, key
, DNSSECKeeper::DIGEST_SHA256
).getZoneRepresentation() << " ; ( SHA256 digest )" << endl
;
1654 string output
=makeDSFromDNSKey(zone
, key
, DNSSECKeeper::DIGEST_GOST
).getZoneRepresentation();
1655 cout
<<prefix
<<zone
.toString()<<" IN DS "<<output
<< " ; ( GOST R 34.11-94 digest )" << endl
;
1660 string output
=makeDSFromDNSKey(zone
, key
, DNSSECKeeper::DIGEST_SHA384
).getZoneRepresentation();
1661 cout
<<prefix
<<zone
.toString()<<" IN DS "<<output
<< " ; ( SHA-384 digest )" << endl
;
1667 else if(keyset
.empty()) {
1668 cerr
<< "No keys for zone '"<<zone
<<"'."<<endl
;
1673 cout
<<"Zone has NSEC semantics"<<endl
;
1675 cout
<<"Zone has " << (narrow
? "NARROW " : "") <<"hashed NSEC3 semantics, configuration: "<<ns3pr
.getZoneRepresentation()<<endl
;
1676 cout
<< "keys: "<<endl
;
1679 for(DNSSECKeeper::keyset_t::value_type value
: keyset
) {
1680 string algname
= DNSSECKeeper::algorithm2name(value
.first
.d_algorithm
);
1682 cout
<<"ID = "<<value
.second
.id
<<" ("<<DNSSECKeeper::keyTypeToString(value
.second
.keyType
)<<")";
1684 if (value
.first
.getKey()->getBits() < 1) {
1685 cerr
<<" <key missing or defunct>" <<endl
;
1689 cout
<<", flags = "<<std::to_string(value
.first
.d_flags
);
1690 cout
<<", tag = "<<value
.first
.getDNSKEY().getTag();
1691 cout
<<", algo = "<<(int)value
.first
.d_algorithm
<<", bits = "<<value
.first
.getKey()->getBits()<<"\t"<<((int)value
.second
.active
== 1 ? " A" : "Ina")<<"ctive\t"<<(value
.second
.published
? " Published" : " Unpublished")<<" ( " + algname
+ " ) "<<endl
;
1695 if (value
.second
.keyType
== DNSSECKeeper::KSK
|| value
.second
.keyType
== DNSSECKeeper::CSK
|| ::arg().mustDo("direct-dnskey")) {
1696 cout
<<DNSSECKeeper::keyTypeToString(value
.second
.keyType
)<<" DNSKEY = "<<zone
.toString()<<" IN DNSKEY "<< value
.first
.getDNSKEY().getZoneRepresentation() << " ; ( " + algname
+ " )" << endl
;
1699 if (value
.second
.keyType
== DNSSECKeeper::KSK
|| value
.second
.keyType
== DNSSECKeeper::CSK
) {
1700 const auto &key
= value
.first
.getDNSKEY();
1701 const std::string
prefix(exportDS
? "" : "DS = ");
1702 cout
<<prefix
<<zone
.toString()<<" IN DS "<<makeDSFromDNSKey(zone
, key
, DNSSECKeeper::DIGEST_SHA1
).getZoneRepresentation() << " ; ( SHA1 digest )" << endl
;
1703 cout
<<prefix
<<zone
.toString()<<" IN DS "<<makeDSFromDNSKey(zone
, key
, DNSSECKeeper::DIGEST_SHA256
).getZoneRepresentation() << " ; ( SHA256 digest )" << endl
;
1705 string output
=makeDSFromDNSKey(zone
, key
, DNSSECKeeper::DIGEST_GOST
).getZoneRepresentation();
1706 cout
<<prefix
<<zone
.toString()<<" IN DS "<<output
<< " ; ( GOST R 34.11-94 digest )" << endl
;
1711 string output
=makeDSFromDNSKey(zone
, key
, DNSSECKeeper::DIGEST_SHA384
).getZoneRepresentation();
1712 cout
<<prefix
<<zone
.toString()<<" IN DS "<<output
<< " ; ( SHA-384 digest )" << endl
;
1722 static bool secureZone(DNSSECKeeper
& dk
, const DNSName
& zone
)
1727 // temp var for addKey
1730 string k_algo
= ::arg()["default-ksk-algorithm"];
1731 k_size
= ::arg().asNum("default-ksk-size");
1732 string z_algo
= ::arg()["default-zsk-algorithm"];
1733 z_size
= ::arg().asNum("default-zsk-size");
1736 throw runtime_error("KSK key size must be equal to or greater than 0");
1739 if (k_algo
== "" && z_algo
== "") {
1740 throw runtime_error("Zero algorithms given for KSK+ZSK in total");
1744 throw runtime_error("ZSK key size must be equal to or greater than 0");
1747 if(dk
.isSecuredZone(zone
)) {
1748 cerr
<< "Zone '"<<zone
<<"' already secure, remove keys with pdnsutil remove-zone-key if needed"<<endl
;
1753 UeberBackend
B("default");
1754 if(!B
.getDomainInfo(zone
, di
) || !di
.backend
) { // di.backend and B are mostly identical
1755 cerr
<<"Can't find a zone called '"<<zone
<<"'"<<endl
;
1759 if(di
.kind
== DomainInfo::Slave
)
1761 cerr
<<"Warning! This is a slave domain! If this was a mistake, please run"<<endl
;
1762 cerr
<<"pdnsutil disable-dnssec "<<zone
<<" right now!"<<endl
;
1765 if (k_algo
!= "") { // Add a KSK
1767 cout
<< "Securing zone with key size " << k_size
<< endl
;
1769 cout
<< "Securing zone with default key size" << endl
;
1771 cout
<< "Adding "<<(z_algo
== "" ? "CSK (257)" : "KSK")<<" with algorithm " << k_algo
<< endl
;
1773 int k_real_algo
= DNSSECKeeper::shorthand2algorithm(k_algo
);
1775 if (!dk
.addKey(zone
, true, k_real_algo
, id
, k_size
, true, true)) {
1776 cerr
<<"No backend was able to secure '"<<zone
<<"', most likely because no DNSSEC"<<endl
;
1777 cerr
<<"capable backends are loaded, or because the backends have DNSSEC disabled."<<endl
;
1778 cerr
<<"For the Generic SQL backends, set the 'gsqlite3-dnssec', 'gmysql-dnssec' or"<<endl
;
1779 cerr
<<"'gpgsql-dnssec' flag. Also make sure the schema has been updated for DNSSEC!"<<endl
;
1785 cout
<< "Adding "<<(k_algo
== "" ? "CSK (256)" : "ZSK")<<" with algorithm " << z_algo
<< endl
;
1787 int z_real_algo
= DNSSECKeeper::shorthand2algorithm(z_algo
);
1789 if (!dk
.addKey(zone
, false, z_real_algo
, id
, z_size
, true, true)) {
1790 cerr
<<"No backend was able to secure '"<<zone
<<"', most likely because no DNSSEC"<<endl
;
1791 cerr
<<"capable backends are loaded, or because the backends have DNSSEC disabled."<<endl
;
1792 cerr
<<"For the Generic SQL backends, set the 'gsqlite3-dnssec', 'gmysql-dnssec' or"<<endl
;
1793 cerr
<<"'gpgsql-dnssec' flag. Also make sure the schema has been updated for DNSSEC!"<<endl
;
1798 if(!dk
.isSecuredZone(zone
)) {
1799 cerr
<<"Failed to secure zone. Is your backend dnssec enabled? (set "<<endl
;
1800 cerr
<<"gsqlite3-dnssec, or gmysql-dnssec etc). Check this first."<<endl
;
1801 cerr
<<"If you run with the BIND backend, make sure you have configured"<<endl
;
1802 cerr
<<"it to use DNSSEC with 'bind-dnssec-db=/path/fname' and"<<endl
;
1803 cerr
<<"'pdnsutil create-bind-db /path/fname'!"<<endl
;
1807 // rectifyZone(dk, zone);
1808 // showZone(dk, zone);
1809 cout
<<"Zone "<<zone
<<" secured"<<endl
;
1813 static void testSchema(DNSSECKeeper
& dk
, const DNSName
& zone
)
1815 cout
<<"Note: test-schema will try to create the zone, but it will not remove it."<<endl
;
1816 cout
<<"Please clean up after this."<<endl
;
1818 cout
<<"If this test reports an error and aborts, please check your database schema."<<endl
;
1819 cout
<<"Constructing UeberBackend"<<endl
;
1820 UeberBackend
B("default");
1821 cout
<<"Picking first backend - if this is not what you want, edit launch line!"<<endl
;
1822 DNSBackend
*db
= B
.backends
[0];
1823 cout
<<"Creating slave domain "<<zone
<<endl
;
1824 db
->createSlaveDomain("127.0.0.1", zone
, "", "_testschema");
1825 cout
<<"Slave domain created"<<endl
;
1828 if(!B
.getDomainInfo(zone
, di
) || !di
.backend
) { // di.backend and B are mostly identical
1829 cout
<<"Can't find domain we just created, aborting"<<endl
;
1833 DNSResourceRecord rr
, rrget
;
1834 cout
<<"Starting transaction to feed records"<<endl
;
1835 db
->startTransaction(zone
, di
.id
);
1837 rr
.qtype
=QType::SOA
;
1842 rr
.content
="ns1.example.com. ahu.example.com. 2012081039 7200 3600 1209600 3600";
1843 cout
<<"Feeding SOA"<<endl
;
1844 db
->feedRecord(rr
, DNSName());
1845 rr
.qtype
=QType::TXT
;
1847 rr
.content
="\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"";
1848 cout
<<"Feeding overlong TXT"<<endl
;
1849 db
->feedRecord(rr
, DNSName());
1850 cout
<<"Committing"<<endl
;
1851 db
->commitTransaction();
1852 cout
<<"Querying TXT"<<endl
;
1853 db
->lookup(QType(QType::TXT
), zone
, di
.id
);
1856 DNSResourceRecord rrthrowaway
;
1857 if(db
->get(rrthrowaway
)) // should not touch rr but don't assume anything
1859 cout
<<"Expected one record, got multiple, aborting"<<endl
;
1862 int size
=rrget
.content
.size();
1865 cout
<<"Expected 302 bytes, got "<<size
<<", aborting"<<endl
;
1869 cout
<<"[+] content field is over 255 bytes"<<endl
;
1871 cout
<<"Dropping all records, inserting SOA+2xA"<<endl
;
1872 db
->startTransaction(zone
, di
.id
);
1874 rr
.qtype
=QType::SOA
;
1879 rr
.content
="ns1.example.com. ahu.example.com. 2012081039 7200 3600 1209600 3600";
1880 cout
<<"Feeding SOA"<<endl
;
1881 db
->feedRecord(rr
, DNSName());
1884 rr
.qname
=DNSName("_underscore")+zone
;
1885 rr
.content
="127.0.0.1";
1886 db
->feedRecord(rr
, DNSName());
1888 rr
.qname
=DNSName("bla")+zone
;
1889 cout
<<"Committing"<<endl
;
1890 db
->commitTransaction();
1892 cout
<<"Securing zone"<<endl
;
1893 secureZone(dk
, zone
);
1894 cout
<<"Rectifying zone"<<endl
;
1895 rectifyZone(dk
, zone
);
1896 cout
<<"Checking underscore ordering"<<endl
;
1897 DNSName before
, after
;
1898 db
->getBeforeAndAfterNames(di
.id
, zone
, DNSName("z")+zone
, before
, after
);
1899 cout
<<"got '"<<before
.toString()<<"' < 'z."<<zone
.toString()<<"' < '"<<after
.toString()<<"'"<<endl
;
1900 if(before
!= DNSName("_underscore")+zone
)
1902 cout
<<"before is wrong, got '"<<before
.toString()<<"', expected '_underscore."<<zone
.toString()<<"', aborting"<<endl
;
1907 cout
<<"after is wrong, got '"<<after
.toString()<<"', expected '"<<zone
.toString()<<"', aborting"<<endl
;
1910 cout
<<"[+] ordername sorting is correct for names starting with _"<<endl
;
1911 cout
<<"Setting low notified serial"<<endl
;
1912 db
->setNotified(di
.id
, 500);
1913 db
->getDomainInfo(zone
, di
);
1914 if(di
.notified_serial
!= 500) {
1915 cout
<<"[-] Set serial 500, got back "<<di
.notified_serial
<<", aborting"<<endl
;
1918 cout
<<"Setting serial that needs 32 bits"<<endl
;
1920 db
->setNotified(di
.id
, 2147484148);
1921 } catch(const PDNSException
&pe
) {
1922 cout
<<"While setting serial, got error: "<<pe
.reason
<<endl
;
1923 cout
<<"aborting"<<endl
;
1926 db
->getDomainInfo(zone
, di
);
1927 if(di
.notified_serial
!= 2147484148) {
1928 cout
<<"[-] Set serial 2147484148, got back "<<di
.notified_serial
<<", aborting"<<endl
;
1931 cout
<<"[+] Big serials work correctly"<<endl
;
1934 cout
<<"End of tests, please remove "<<zone
<<" from domains+records"<<endl
;
1937 static int addOrSetMeta(const DNSName
& zone
, const string
& kind
, const vector
<string
>& values
, bool clobber
) {
1938 UeberBackend
B("default");
1941 if (!B
.getDomainInfo(zone
, di
)) {
1942 cerr
<< "Invalid zone '" << zone
<< "'" << endl
;
1946 vector
<string
> all_metadata
;
1949 B
.getDomainMetadata(zone
, kind
, all_metadata
);
1952 all_metadata
.insert(all_metadata
.end(), values
.begin(), values
.end());
1954 if (!B
.setDomainMetadata(zone
, kind
, all_metadata
)) {
1955 cerr
<< "Unable to set meta for '" << zone
<< "'" << endl
;
1959 cout
<< "Set '" << zone
<< "' meta " << kind
<< " = " << boost::join(all_metadata
, ", ") << endl
;
1963 int main(int argc
, char** argv
)
1966 po::options_description
desc("Allowed options");
1968 ("help,h", "produce help message")
1969 ("version", "show version")
1970 ("verbose,v", "be verbose")
1971 ("force", "force an action")
1972 ("config-name", po::value
<string
>()->default_value(""), "virtual configuration name")
1973 ("config-dir", po::value
<string
>()->default_value(SYSCONFDIR
), "location of pdns.conf")
1974 ("commands", po::value
<vector
<string
> >());
1976 po::positional_options_description p
;
1977 p
.add("commands", -1);
1978 po::store(po::command_line_parser(argc
, argv
).options(desc
).positional(p
).run(), g_vm
);
1981 vector
<string
> cmds
;
1983 if(g_vm
.count("commands"))
1984 cmds
= g_vm
["commands"].as
<vector
<string
> >();
1986 g_verbose
= g_vm
.count("verbose");
1988 if (g_vm
.count("version")) {
1989 cout
<<"pdnsutil "<<VERSION
<<endl
;
1993 if(cmds
.empty() || g_vm
.count("help") || cmds
[0] == "help") {
1994 cout
<<"Usage: \npdnsutil [options] <command> [params ..]\n"<<endl
;
1995 cout
<<"Commands:"<<endl
;
1996 cout
<<"activate-tsig-key ZONE NAME {master|slave}"<<endl
;
1997 cout
<<" Enable TSIG authenticated AXFR using the key NAME for ZONE"<<endl
;
1998 cout
<<"activate-zone-key ZONE KEY-ID Activate the key with key id KEY-ID in ZONE"<<endl
;
1999 cout
<<"add-record ZONE NAME TYPE [ttl] content"<<endl
;
2000 cout
<<" [content..] Add one or more records to ZONE"<<endl
;
2001 cout
<<"add-zone-key ZONE {zsk|ksk} [BITS] [active|inactive] [published|unpublished]"<<endl
;
2002 cout
<<" [rsasha1|rsasha1-nsec3-sha1|rsasha256|rsasha512|ecdsa256|ecdsa384";
2003 #if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBDECAF) || defined(HAVE_LIBCRYPTO_ED25519)
2006 #if defined(HAVE_LIBDECAF) || defined(HAVE_LIBCRYPTO_ED448)
2010 cout
<<" Add a ZSK or KSK to zone and specify algo&bits"<<endl
;
2011 cout
<<"backend-cmd BACKEND CMD [CMD..] Perform one or more backend commands"<<endl
;
2012 cout
<<"b2b-migrate OLD NEW Move all data from one backend to another"<<endl
;
2013 cout
<<"bench-db [filename] Bench database backend with queries, one domain per line"<<endl
;
2014 cout
<<"check-zone ZONE Check a zone for correctness"<<endl
;
2015 cout
<<"check-all-zones [exit-on-error] Check all zones for correctness. Set exit-on-error to exit immediately"<<endl
;
2016 cout
<<" after finding an error in a zone."<<endl
;
2017 cout
<<"clear-zone ZONE Clear all records of a zone, but keep everything else"<<endl
;
2018 cout
<<"create-bind-db FNAME Create DNSSEC db for BIND backend (bind-dnssec-db)"<<endl
;
2019 cout
<<"create-slave-zone ZONE master-ip [master-ip..]"<<endl
;
2020 cout
<<" Create slave zone ZONE with master IP address master-ip"<<endl
;
2021 cout
<<"change-slave-zone-master ZONE master-ip [master-ip..]"<<endl
;
2022 cout
<<" Change slave zone ZONE master IP address to master-ip"<<endl
;
2023 cout
<<"create-zone ZONE [nsname] Create empty zone ZONE"<<endl
;
2024 cout
<<"deactivate-tsig-key ZONE NAME {master|slave}"<<endl
;
2025 cout
<<" Disable TSIG authenticated AXFR using the key NAME for ZONE"<<endl
;
2026 cout
<<"deactivate-zone-key ZONE KEY-ID Deactivate the key with key id KEY-ID in ZONE"<<endl
;
2027 cout
<<"delete-rrset ZONE NAME TYPE Delete named RRSET from zone"<<endl
;
2028 cout
<<"delete-tsig-key NAME Delete TSIG key (warning! will not unmap key!)"<<endl
;
2029 cout
<<"delete-zone ZONE Delete the zone"<<endl
;
2030 cout
<<"disable-dnssec ZONE Deactivate all keys and unset PRESIGNED in ZONE"<<endl
;
2031 cout
<<"edit-zone ZONE Edit zone contents using $EDITOR"<<endl
;
2032 cout
<<"export-zone-dnskey ZONE KEY-ID Export to stdout the public DNSKEY described"<<endl
;
2033 cout
<<"export-zone-ds ZONE Export to stdout all KSK DS records for ZONE"<<endl
;
2034 cout
<<"export-zone-key ZONE KEY-ID Export to stdout the private key described"<<endl
;
2035 cout
<<"generate-tsig-key NAME ALGORITHM Generate new TSIG key"<<endl
;
2036 cout
<<"generate-zone-key {zsk|ksk} [ALGORITHM] [BITS]"<<endl
;
2037 cout
<<" Generate a ZSK or KSK to stdout with specified ALGORITHM and BITS"<<endl
;
2038 cout
<<"get-meta ZONE [KIND ...] Get zone metadata. If no KIND given, lists all known"<<endl
;
2039 cout
<<"hash-zone-record ZONE RNAME Calculate the NSEC3 hash for RNAME in ZONE"<<endl
;
2041 cout
<<"hsm assign ZONE ALGORITHM {ksk|zsk} MODULE SLOT PIN LABEL"<<endl
<<
2042 " Assign a hardware signing module to a ZONE"<<endl
;
2043 cout
<<"hsm create-key ZONE KEY-ID [BITS] Create a key using hardware signing module for ZONE (use assign first)"<<endl
;
2044 cout
<<" BITS defaults to 2048"<<endl
;
2046 cout
<<"increase-serial ZONE Increases the SOA-serial by 1. Uses SOA-EDIT"<<endl
;
2047 cout
<<"import-tsig-key NAME ALGORITHM KEY Import TSIG key"<<endl
;
2048 cout
<<"import-zone-key ZONE FILE Import from a file a private key, ZSK or KSK"<<endl
;
2049 cout
<<" [active|inactive] [ksk|zsk] [published|unpublished] Defaults to KSK, active and published"<<endl
;
2050 cout
<<"ipdecrypt IP passphrase/key [key] Encrypt IP address using passphrase or base64 key"<<endl
;
2051 cout
<<"ipencrypt IP passphrase/key [key] Encrypt IP address using passphrase or base64 key"<<endl
;
2052 cout
<<"load-zone ZONE FILE Load ZONE from FILE, possibly creating zone or atomically"<<endl
;
2053 cout
<<" replacing contents"<<endl
;
2054 cout
<<"list-algorithms [with-backend] List all DNSSEC algorithms supported, optionally also listing the crypto library used"<<endl
;
2055 cout
<<"list-keys [ZONE] List DNSSEC keys for ZONE. When ZONE is unset or \"all\", display all keys for all zones"<<endl
;
2056 cout
<<"list-zone ZONE List zone contents"<<endl
;
2057 cout
<<"list-all-zones [master|slave|native]"<<endl
;
2058 cout
<<" List all zone names"<<endl
;;
2059 cout
<<"list-tsig-keys List all TSIG keys"<<endl
;
2060 cout
<<"publish-zone-key ZONE KEY-ID Publish the zone key with key id KEY-ID in ZONE"<<endl
;
2061 cout
<<"rectify-zone ZONE [ZONE ..] Fix up DNSSEC fields (order, auth)"<<endl
;
2062 cout
<<"rectify-all-zones [quiet] Rectify all zones. Optionally quiet output with errors only"<<endl
;
2063 cout
<<"remove-zone-key ZONE KEY-ID Remove key with KEY-ID from ZONE"<<endl
;
2064 cout
<<"replace-rrset ZONE NAME TYPE [ttl] Replace named RRSET from zone"<<endl
;
2065 cout
<<" content [content..]"<<endl
;
2066 cout
<<"secure-all-zones [increase-serial] Secure all zones without keys"<<endl
;
2067 cout
<<"secure-zone ZONE [ZONE ..] Add DNSSEC to zone ZONE"<<endl
;
2068 cout
<<"set-kind ZONE KIND Change the kind of ZONE to KIND (master, slave, native)"<<endl
;
2069 cout
<<"set-account ZONE ACCOUNT Change the account (owner) of ZONE to ACCOUNT"<<endl
;
2070 cout
<<"set-nsec3 ZONE ['PARAMS' [narrow]] Enable NSEC3 with PARAMS. Optionally narrow"<<endl
;
2071 cout
<<"set-presigned ZONE Use presigned RRSIGs from storage"<<endl
;
2072 cout
<<"set-publish-cdnskey ZONE Enable sending CDNSKEY responses for ZONE"<<endl
;
2073 cout
<<"set-publish-cds ZONE [DIGESTALGOS] Enable sending CDS responses for ZONE, using DIGESTALGOS as signature algorithms"<<endl
;
2074 cout
<<" DIGESTALGOS should be a comma separated list of numbers, it is '2' by default"<<endl
;
2075 cout
<<"add-meta ZONE KIND VALUE Add zone metadata, this adds to the existing KIND"<<endl
;
2076 cout
<<" [VALUE ...]"<<endl
;
2077 cout
<<"set-meta ZONE KIND [VALUE] [VALUE] Set zone metadata, optionally providing a value. *No* value clears meta"<<endl
;
2078 cout
<<" Note - this will replace all metadata records of KIND!"<<endl
;
2079 cout
<<"show-zone ZONE Show DNSSEC (public) key details about a zone"<<endl
;
2080 cout
<<"unpublish-zone-key ZONE KEY-ID Unpublish the zone key with key id KEY-ID in ZONE"<<endl
;
2081 cout
<<"unset-nsec3 ZONE Switch back to NSEC"<<endl
;
2082 cout
<<"unset-presigned ZONE No longer use presigned RRSIGs"<<endl
;
2083 cout
<<"unset-publish-cdnskey ZONE Disable sending CDNSKEY responses for ZONE"<<endl
;
2084 cout
<<"unset-publish-cds ZONE Disable sending CDS responses for ZONE"<<endl
;
2085 cout
<<"test-schema ZONE Test DB schema - will create ZONE"<<endl
;
2090 loadMainConfig(g_vm
["config-dir"].as
<string
>());
2092 if (cmds
[0] == "test-algorithm") {
2093 if(cmds
.size() != 2) {
2094 cerr
<< "Syntax: pdnsutil test-algorithm algonum"<<endl
;
2097 if (testAlgorithm(pdns_stou(cmds
[1])))
2102 if(cmds
[0] == "ipencrypt" || cmds
[0]=="ipdecrypt") {
2103 if(cmds
.size() < 3 || (cmds
.size()== 4 && cmds
[3]!="key")) {
2104 cerr
<<"Syntax: pdnsutil [ipencrypt|ipdecrypt] IP passphrase [key]"<<endl
;
2108 if(cmds
.size()==4) {
2109 if(B64Decode(cmds
[2], key
) < 0) {
2110 cerr
<<"Could not parse '"<<cmds
[3]<<"' as base64"<<endl
;
2115 key
= makeIPCipherKey(cmds
[2]);
2117 exit(xcryptIP(cmds
[0], cmds
[1], key
));
2121 if(cmds
[0] == "test-algorithms") {
2122 if (testAlgorithms())
2127 if(cmds
[0] == "list-algorithms") {
2128 if((cmds
.size() == 2 && cmds
[1] != "with-backend") || cmds
.size() > 2) {
2129 cerr
<<"Syntax: pdnsutil list-algorithms [with-backend]"<<endl
;
2133 cout
<<"DNSKEY algorithms supported by this installation of PowerDNS:"<<endl
;
2135 auto algosWithBackend
= DNSCryptoKeyEngine::listAllAlgosWithBackend();
2136 for (const auto& algoWithBackend
: algosWithBackend
){
2137 string algoName
= DNSSECKeeper::algorithm2name(algoWithBackend
.first
);
2138 cout
<<std::to_string(algoWithBackend
.first
)<<" - "<<algoName
;
2139 if (cmds
.size() == 2 && cmds
[1] == "with-backend")
2140 cout
<<" using "<<algoWithBackend
.second
;
2148 if(cmds
[0] == "create-bind-db") {
2150 if(cmds
.size() != 2) {
2151 cerr
<< "Syntax: pdnsutil create-bind-db FNAME"<<endl
;
2155 SSQLite3
db(cmds
[1], "", true); // create=ok
2156 vector
<string
> statements
;
2157 stringtok(statements
, sqlCreate
, ";");
2158 for(const string
& statement
: statements
) {
2159 db
.execute(statement
);
2162 catch(SSqlException
& se
) {
2163 throw PDNSException("Error creating database in BIND backend: "+se
.txtReason());
2167 cerr
<<"bind-dnssec-db requires building PowerDNS with SQLite3"<<endl
;
2174 if (cmds
[0] == "test-schema") {
2175 if(cmds
.size() != 2) {
2176 cerr
<< "Syntax: pdnsutil test-schema ZONE"<<endl
;
2179 testSchema(dk
, DNSName(cmds
[1]));
2182 if(cmds
[0] == "rectify-zone") {
2183 if(cmds
.size() < 2) {
2184 cerr
<< "Syntax: pdnsutil rectify-zone ZONE [ZONE..]"<<endl
;
2187 unsigned int exitCode
= 0;
2188 for(unsigned int n
= 1; n
< cmds
.size(); ++n
)
2189 if (!rectifyZone(dk
, DNSName(cmds
[n
])))
2193 else if (cmds
[0] == "rectify-all-zones") {
2194 bool quiet
= (cmds
.size() >= 2 && cmds
[1] == "quiet");
2195 if (!rectifyAllZones(dk
, quiet
)) {
2199 else if(cmds
[0] == "check-zone") {
2200 if(cmds
.size() != 2) {
2201 cerr
<< "Syntax: pdnsutil check-zone ZONE"<<endl
;
2204 UeberBackend
B("default");
2205 exit(checkZone(dk
, B
, DNSName(cmds
[1])));
2207 else if(cmds
[0] == "bench-db") {
2208 dbBench(cmds
.size() > 1 ? cmds
[1] : "");
2210 else if (cmds
[0] == "check-all-zones") {
2211 bool exitOnError
= ((cmds
.size() >= 2 ? cmds
[1] : "") == "exit-on-error");
2212 exit(checkAllZones(dk
, exitOnError
));
2214 else if (cmds
[0] == "list-all-zones") {
2215 if (cmds
.size() > 2) {
2216 cerr
<< "Syntax: pdnsutil list-all-zones [master|slave|native]"<<endl
;
2219 if (cmds
.size() == 2)
2220 return listAllZones(cmds
[1]);
2221 return listAllZones();
2223 else if (cmds
[0] == "test-zone") {
2224 cerr
<< "Did you mean check-zone?"<<endl
;
2227 else if (cmds
[0] == "test-all-zones") {
2228 cerr
<< "Did you mean check-all-zones?"<<endl
;
2232 else if(cmds
[0] == "signing-server" )
2236 else if(cmds
[0] == "signing-slave")
2238 launchSigningService(0);
2241 else if(cmds
[0] == "test-speed") {
2242 if(cmds
.size() < 2) {
2243 cerr
<< "Syntax: pdnsutil test-speed numcores [signing-server]"<<endl
;
2246 testSpeed(dk
, DNSName(cmds
[1]), (cmds
.size() > 3) ? cmds
[3] : "", pdns_stou(cmds
[2]));
2248 else if(cmds
[0] == "verify-crypto") {
2249 if(cmds
.size() != 2) {
2250 cerr
<< "Syntax: pdnsutil verify-crypto FILE"<<endl
;
2253 verifyCrypto(cmds
[1]);
2255 else if(cmds
[0] == "show-zone") {
2256 if(cmds
.size() != 2) {
2257 cerr
<< "Syntax: pdnsutil show-zone ZONE"<<endl
;
2260 if (!showZone(dk
, DNSName(cmds
[1]))) return 1;
2262 else if(cmds
[0] == "export-zone-ds") {
2263 if(cmds
.size() != 2) {
2264 cerr
<< "Syntax: pdnsutil export-zone-ds ZONE"<<endl
;
2267 if (!showZone(dk
, DNSName(cmds
[1]), true)) return 1;
2269 else if(cmds
[0] == "disable-dnssec") {
2270 if(cmds
.size() != 2) {
2271 cerr
<< "Syntax: pdnsutil disable-dnssec ZONE"<<endl
;
2274 DNSName
zone(cmds
[1]);
2275 if(!disableDNSSECOnZone(dk
, zone
)) {
2276 cerr
<< "Cannot disable DNSSEC on " << zone
<< endl
;
2280 else if(cmds
[0] == "activate-zone-key") {
2281 if(cmds
.size() != 3) {
2282 cerr
<< "Syntax: pdnsutil activate-zone-key ZONE KEY-ID"<<endl
;
2285 DNSName
zone(cmds
[1]);
2286 unsigned int id
=atoi(cmds
[2].c_str()); // if you make this pdns_stou, the error gets worse
2289 cerr
<<"Invalid KEY-ID '"<<cmds
[2]<<"'"<<endl
;
2292 if (!dk
.activateKey(zone
, id
)) {
2293 cerr
<<"Activation of key failed"<<endl
;
2298 else if(cmds
[0] == "deactivate-zone-key") {
2299 if(cmds
.size() != 3) {
2300 cerr
<< "Syntax: pdnsutil deactivate-zone-key ZONE KEY-ID"<<endl
;
2303 DNSName
zone(cmds
[1]);
2304 unsigned int id
=pdns_stou(cmds
[2]);
2307 cerr
<<"Invalid KEY-ID"<<endl
;
2310 if (!dk
.deactivateKey(zone
, id
)) {
2311 cerr
<<"Deactivation of key failed"<<endl
;
2316 else if(cmds
[0] == "publish-zone-key") {
2317 if(cmds
.size() != 3) {
2318 cerr
<< "Syntax: pdnsutil publish-zone-key ZONE KEY-ID"<<endl
;
2321 DNSName
zone(cmds
[1]);
2322 unsigned int id
=atoi(cmds
[2].c_str()); // if you make this pdns_stou, the error gets worse
2325 cerr
<<"Invalid KEY-ID '"<<cmds
[2]<<"'"<<endl
;
2328 if (!dk
.publishKey(zone
, id
)) {
2329 cerr
<<"Publishing of key failed"<<endl
;
2334 else if(cmds
[0] == "unpublish-zone-key") {
2335 if(cmds
.size() != 3) {
2336 cerr
<< "Syntax: pdnsutil unpublish-zone-key ZONE KEY-ID"<<endl
;
2339 DNSName
zone(cmds
[1]);
2340 unsigned int id
=atoi(cmds
[2].c_str()); // if you make this pdns_stou, the error gets worse
2343 cerr
<<"Invalid KEY-ID '"<<cmds
[2]<<"'"<<endl
;
2346 if (!dk
.unpublishKey(zone
, id
)) {
2347 cerr
<<"Unpublishing of key failed"<<endl
;
2353 else if(cmds
[0] == "add-zone-key") {
2354 if(cmds
.size() < 3 ) {
2355 cerr
<< "Syntax: pdnsutil add-zone-key ZONE zsk|ksk [BITS] [active|inactive] [rsasha1|rsasha1-nsec3-sha1|rsasha256|rsasha512|ecdsa256|ecdsa384";
2356 #if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBDECAF) || defined(HAVE_LIBCRYPTO_ED25519)
2359 #if defined(HAVE_LIBDECAF) || defined(HAVE_LIBCRYPTO_ED448)
2365 DNSName
zone(cmds
[1]);
2367 UeberBackend
B("default");
2370 if (!B
.getDomainInfo(zone
, di
)){
2371 cerr
<< "No such zone in the database" << endl
;
2375 // need to get algorithm, bits & ksk or zsk from commandline
2376 bool keyOrZone
=false;
2379 int algorithm
=DNSSECKeeper::ECDSA256
;
2381 bool published
=true;
2382 for(unsigned int n
=2; n
< cmds
.size(); ++n
) {
2383 if(pdns_iequals(cmds
[n
], "zsk"))
2385 else if(pdns_iequals(cmds
[n
], "ksk"))
2387 else if((tmp_algo
= DNSSECKeeper::shorthand2algorithm(cmds
[n
]))>0) {
2388 algorithm
= tmp_algo
;
2389 } else if(pdns_iequals(cmds
[n
], "active")) {
2391 } else if(pdns_iequals(cmds
[n
], "inactive") || pdns_iequals(cmds
[n
], "passive")) { // 'passive' eventually needs to be removed
2393 } else if(pdns_iequals(cmds
[n
], "published")) {
2395 } else if(pdns_iequals(cmds
[n
], "unpublished")) {
2397 } else if(pdns_stou(cmds
[n
])) {
2398 bits
= pdns_stou(cmds
[n
]);
2400 cerr
<<"Unknown algorithm, key flag or size '"<<cmds
[n
]<<"'"<<endl
;
2401 exit(EXIT_FAILURE
);;
2405 if (!dk
.addKey(zone
, keyOrZone
, algorithm
, id
, bits
, active
, published
)) {
2406 cerr
<<"Adding key failed, perhaps DNSSEC not enabled in configuration?"<<endl
;
2409 cerr
<<"Added a " << (keyOrZone
? "KSK" : "ZSK")<<" with algorithm = "<<algorithm
<<", active="<<active
<<endl
;
2411 cerr
<<"Requested specific key size of "<<bits
<<" bits"<<endl
;
2413 cerr
<<std::to_string(id
)<<"Key was added, but backend does not support returning of key id"<<endl
;
2414 } else if (id
< -1) {
2415 cerr
<<std::to_string(id
)<<"Key was added, but there was a failure while returning the key id"<<endl
;
2417 cout
<<std::to_string(id
)<<endl
;
2421 else if(cmds
[0] == "remove-zone-key") {
2422 if(cmds
.size() < 3) {
2423 cerr
<<"Syntax: pdnsutil remove-zone-key ZONE KEY-ID"<<endl
;
2426 DNSName
zone(cmds
[1]);
2427 unsigned int id
=pdns_stou(cmds
[2]);
2428 if (!dk
.removeKey(zone
, id
)) {
2429 cerr
<<"Cannot remove key " << id
<< " from " << zone
<<endl
;
2434 else if(cmds
[0] == "delete-zone") {
2435 if(cmds
.size() != 2) {
2436 cerr
<<"Syntax: pdnsutil delete-zone ZONE"<<endl
;
2439 exit(deleteZone(DNSName(cmds
[1])));
2441 else if(cmds
[0] == "create-zone") {
2442 if(cmds
.size() != 2 && cmds
.size()!=3 ) {
2443 cerr
<<"Syntax: pdnsutil create-zone ZONE [nsname]"<<endl
;
2446 exit(createZone(DNSName(cmds
[1]), cmds
.size() > 2 ? DNSName(cmds
[2]): DNSName()));
2448 else if(cmds
[0] == "create-slave-zone") {
2449 if(cmds
.size() < 3 ) {
2450 cerr
<<"Syntax: pdnsutil create-slave-zone ZONE master-ip [master-ip..]"<<endl
;
2453 exit(createSlaveZone(cmds
));
2455 else if(cmds
[0] == "change-slave-zone-master") {
2456 if(cmds
.size() < 3 ) {
2457 cerr
<<"Syntax: pdnsutil change-slave-zone-master ZONE master-ip [master-ip..]"<<endl
;
2460 exit(changeSlaveZoneMaster(cmds
));
2462 else if(cmds
[0] == "add-record") {
2463 if(cmds
.size() < 5) {
2464 cerr
<<"Syntax: pdnsutil add-record ZONE name type [ttl] \"content\" [\"content\"...]"<<endl
;
2467 exit(addOrReplaceRecord(true, cmds
));
2469 else if(cmds
[0] == "replace-rrset") {
2470 if(cmds
.size() < 5) {
2471 cerr
<<"Syntax: pdnsutil replace-rrset ZONE name type [ttl] \"content\" [\"content\"...]"<<endl
;
2474 exit(addOrReplaceRecord(false , cmds
));
2476 else if(cmds
[0] == "delete-rrset") {
2477 if(cmds
.size() != 4) {
2478 cerr
<<"Syntax: pdnsutil delete-rrset ZONE name type"<<endl
;
2481 exit(deleteRRSet(cmds
[1], cmds
[2], cmds
[3]));
2483 else if(cmds
[0] == "list-zone") {
2484 if(cmds
.size() != 2) {
2485 cerr
<<"Syntax: pdnsutil list-zone ZONE"<<endl
;
2491 exit(listZone(DNSName(cmds
[1])));
2493 else if(cmds
[0] == "edit-zone") {
2494 if(cmds
.size() != 2) {
2495 cerr
<<"Syntax: pdnsutil edit-zone ZONE"<<endl
;
2501 exit(editZone(DNSName(cmds
[1])));
2503 else if(cmds
[0] == "clear-zone") {
2504 if(cmds
.size() != 2) {
2505 cerr
<<"Syntax: pdnsutil edit-zone ZONE"<<endl
;
2511 exit(clearZone(dk
, DNSName(cmds
[1])));
2513 else if(cmds
[0] == "list-keys") {
2514 if(cmds
.size() > 2) {
2515 cerr
<<"Syntax: pdnsutil list-keys [ZONE]"<<endl
;
2518 string zname
= (cmds
.size() == 2) ? cmds
[1] : "all";
2519 exit(listKeys(zname
, dk
));
2521 else if(cmds
[0] == "load-zone") {
2522 if(cmds
.size() < 3) {
2523 cerr
<<"Syntax: pdnsutil load-zone ZONE FILENAME [ZONE FILENAME] .."<<endl
;
2529 for(size_t n
=1; n
+ 2 <= cmds
.size(); n
+=2)
2530 loadZone(DNSName(cmds
[n
]), cmds
[n
+1]);
2533 else if(cmds
[0] == "secure-zone") {
2534 if(cmds
.size() < 2) {
2535 cerr
<< "Syntax: pdnsutil secure-zone ZONE"<<endl
;
2538 vector
<DNSName
> mustRectify
;
2539 unsigned int zoneErrors
=0;
2540 for(unsigned int n
= 1; n
< cmds
.size(); ++n
) {
2541 DNSName
zone(cmds
[n
]);
2542 dk
.startTransaction(zone
, -1);
2543 if(secureZone(dk
, zone
)) {
2544 mustRectify
.push_back(zone
);
2548 dk
.commitTransaction();
2551 for(const auto& zone
: mustRectify
)
2552 rectifyZone(dk
, zone
);
2559 else if (cmds
[0] == "secure-all-zones") {
2560 if (cmds
.size() >= 2 && !pdns_iequals(cmds
[1], "increase-serial")) {
2561 cerr
<< "Syntax: pdnsutil secure-all-zones [increase-serial]"<<endl
;
2565 UeberBackend
B("default");
2567 vector
<DomainInfo
> domainInfo
;
2568 B
.getAllDomains(&domainInfo
);
2570 unsigned int zonesSecured
=0, zoneErrors
=0;
2571 for(DomainInfo di
: domainInfo
) {
2572 if(!dk
.isSecuredZone(di
.zone
)) {
2573 cout
<<"Securing "<<di
.zone
<<": ";
2574 if (secureZone(dk
, di
.zone
)) {
2576 if (cmds
.size() == 2) {
2577 if (!increaseSerial(di
.zone
, dk
))
2586 cout
<<"Secured: "<<zonesSecured
<<" zones. Errors: "<<zoneErrors
<<endl
;
2593 else if(cmds
[0]=="set-kind") {
2594 if(cmds
.size() != 3) {
2595 cerr
<<"Syntax: pdnsutil set-kind ZONE KIND"<<endl
;
2598 DNSName
zone(cmds
[1]);
2599 auto kind
=DomainInfo::stringToKind(cmds
[2]);
2600 exit(setZoneKind(zone
, kind
));
2602 else if(cmds
[0]=="set-account") {
2603 if(cmds
.size() != 3) {
2604 cerr
<<"Syntax: pdnsutil set-account ZONE ACCOUNT"<<endl
;
2607 DNSName
zone(cmds
[1]);
2608 exit(setZoneAccount(zone
, cmds
[2]));
2610 else if(cmds
[0]=="set-nsec3") {
2611 if(cmds
.size() < 2) {
2612 cerr
<<"Syntax: pdnsutil set-nsec3 ZONE 'params' [narrow]"<<endl
;
2615 string nsec3params
= cmds
.size() > 2 ? cmds
[2] : "1 0 1 ab";
2616 bool narrow
= cmds
.size() > 3 && cmds
[3]=="narrow";
2617 NSEC3PARAMRecordContent
ns3pr(nsec3params
);
2619 DNSName
zone(cmds
[1]);
2620 if (zone
.wirelength() > 222) {
2621 cerr
<<"Cannot enable NSEC3 for " << zone
<< " as it is too long (" << zone
.wirelength() << " bytes, maximum is 222 bytes)"<<endl
;
2624 if(ns3pr
.d_algorithm
!= 1) {
2625 cerr
<<"NSEC3PARAM algorithm set to '"<<std::to_string(ns3pr
.d_algorithm
)<<"', but '1' is the only valid value"<<endl
;
2626 return EXIT_FAILURE
;
2628 if (! dk
.setNSEC3PARAM(zone
, ns3pr
, narrow
)) {
2629 cerr
<<"Cannot set NSEC3 param for " << zone
<< endl
;
2634 cerr
<<"NSEC3 set, ";
2636 cerr
<<"NSEC3 (opt-out) set, ";
2638 if(dk
.isSecuredZone(zone
))
2639 cerr
<<"please rectify your zone if your backend needs it"<<endl
;
2641 cerr
<<"please secure and rectify your zone."<<endl
;
2645 else if(cmds
[0]=="set-presigned") {
2646 if(cmds
.size() < 2) {
2647 cerr
<<"Syntax: pdnsutil set-presigned ZONE"<<endl
;
2650 if (! dk
.setPresigned(DNSName(cmds
[1]))) {
2651 cerr
<< "Could not set presigned for " << cmds
[1] << " (is DNSSEC enabled in your backend?)" << endl
;
2656 else if(cmds
[0]=="set-publish-cdnskey") {
2657 if(cmds
.size() < 2) {
2658 cerr
<<"Syntax: pdnsutil set-publish-cdnskey ZONE"<<endl
;
2661 if (! dk
.setPublishCDNSKEY(DNSName(cmds
[1]))) {
2662 cerr
<< "Could not set publishing for CDNSKEY records for "<< cmds
[1]<<endl
;
2667 else if(cmds
[0]=="set-publish-cds") {
2668 if(cmds
.size() < 2) {
2669 cerr
<<"Syntax: pdnsutil set-publish-cds ZONE [DIGESTALGOS]"<<endl
;
2673 // If DIGESTALGOS is unset
2674 if(cmds
.size() == 2)
2675 cmds
.push_back("2");
2677 if (! dk
.setPublishCDS(DNSName(cmds
[1]), cmds
[2])) {
2678 cerr
<< "Could not set publishing for CDS records for "<< cmds
[1]<<endl
;
2683 else if(cmds
[0]=="unset-presigned") {
2684 if(cmds
.size() < 2) {
2685 cerr
<<"Syntax: pdnsutil unset-presigned ZONE"<<endl
;
2688 if (! dk
.unsetPresigned(DNSName(cmds
[1]))) {
2689 cerr
<< "Could not unset presigned on for " << cmds
[1] << endl
;
2694 else if(cmds
[0]=="unset-publish-cdnskey") {
2695 if(cmds
.size() < 2) {
2696 cerr
<<"Syntax: pdnsutil unset-publish-cdnskey ZONE"<<endl
;
2699 if (! dk
.unsetPublishCDNSKEY(DNSName(cmds
[1]))) {
2700 cerr
<< "Could not unset publishing for CDNSKEY records for "<< cmds
[1]<<endl
;
2705 else if(cmds
[0]=="unset-publish-cds") {
2706 if(cmds
.size() < 2) {
2707 cerr
<<"Syntax: pdnsutil unset-publish-cds ZONE"<<endl
;
2710 if (! dk
.unsetPublishCDS(DNSName(cmds
[1]))) {
2711 cerr
<< "Could not unset publishing for CDS records for "<< cmds
[1]<<endl
;
2716 else if(cmds
[0]=="hash-zone-record") {
2717 if(cmds
.size() < 3) {
2718 cerr
<<"Syntax: pdnsutil hash-zone-record ZONE RNAME"<<endl
;
2721 DNSName
zone(cmds
[1]);
2722 DNSName
record(cmds
[2]);
2723 NSEC3PARAMRecordContent ns3pr
;
2725 if(!dk
.getNSEC3PARAM(zone
, &ns3pr
, &narrow
)) {
2726 cerr
<<"The '"<<zone
<<"' zone does not use NSEC3"<<endl
;
2730 cerr
<<"The '"<<zone
<<"' zone uses narrow NSEC3, but calculating hash anyhow"<<endl
;
2733 cout
<<toBase32Hex(hashQNameWithSalt(ns3pr
, record
))<<endl
;
2735 else if(cmds
[0]=="unset-nsec3") {
2736 if(cmds
.size() < 2) {
2737 cerr
<<"Syntax: pdnsutil unset-nsec3 ZONE"<<endl
;
2740 if ( ! dk
.unsetNSEC3PARAM(DNSName(cmds
[1]))) {
2741 cerr
<<"Cannot unset NSEC3 param for " << cmds
[1] << endl
;
2746 else if(cmds
[0]=="export-zone-key") {
2747 if(cmds
.size() < 3) {
2748 cerr
<<"Syntax: pdnsutil export-zone-key ZONE KEY-ID"<<endl
;
2752 string zone
=cmds
[1];
2753 unsigned int id
=pdns_stou(cmds
[2]);
2754 DNSSECPrivateKey dpk
=dk
.getKeyById(DNSName(zone
), id
);
2755 cout
<< dpk
.getKey()->convertToISC() <<endl
;
2757 else if(cmds
[0]=="increase-serial") {
2758 if (cmds
.size() < 2) {
2759 cerr
<<"Syntax: pdnsutil increase-serial ZONE"<<endl
;
2762 return increaseSerial(DNSName(cmds
[1]), dk
);
2764 else if(cmds
[0]=="import-zone-key-pem") {
2765 if(cmds
.size() < 4) {
2766 cerr
<<"Syntax: pdnsutil import-zone-key-pem ZONE FILE ALGORITHM {ksk|zsk}"<<endl
;
2769 string zone
=cmds
[1];
2770 string fname
=cmds
[2];
2772 ifstream
ifs(fname
.c_str());
2773 string tmp
, interim
, raw
;
2774 while(getline(ifs
, line
)) {
2780 B64Decode(interim
, raw
);
2781 DNSSECPrivateKey dpk
;
2782 DNSKEYRecordContent drc
;
2783 shared_ptr
<DNSCryptoKeyEngine
> key(DNSCryptoKeyEngine::makeFromPEMString(drc
, raw
));
2786 dpk
.d_algorithm
= pdns_stou(cmds
[3]);
2788 if(dpk
.d_algorithm
== DNSSECKeeper::RSASHA1NSEC3SHA1
)
2789 dpk
.d_algorithm
= DNSSECKeeper::RSASHA1
;
2791 cerr
<<(int)dpk
.d_algorithm
<<endl
;
2793 if(cmds
.size() > 4) {
2794 if(pdns_iequals(cmds
[4], "ZSK"))
2796 else if(pdns_iequals(cmds
[4], "KSK"))
2799 cerr
<<"Unknown key flag '"<<cmds
[4]<<"'"<<endl
;
2804 dpk
.d_flags
= 257; // ksk
2807 if (!dk
.addKey(DNSName(zone
), dpk
, id
)) {
2808 cerr
<<"Adding key failed, perhaps DNSSEC not enabled in configuration?"<<endl
;
2812 cerr
<<std::to_string(id
)<<"Key was added, but backend does not support returning of key id"<<endl
;
2813 } else if (id
< -1) {
2814 cerr
<<std::to_string(id
)<<"Key was added, but there was a failure while returning the key id"<<endl
;
2816 cout
<<std::to_string(id
)<<endl
;
2820 else if(cmds
[0]=="import-zone-key") {
2821 if(cmds
.size() < 3) {
2822 cerr
<<"Syntax: pdnsutil import-zone-key ZONE FILE [ksk|zsk] [active|inactive]"<<endl
;
2825 string zone
=cmds
[1];
2826 string fname
=cmds
[2];
2827 DNSSECPrivateKey dpk
;
2828 DNSKEYRecordContent drc
;
2829 shared_ptr
<DNSCryptoKeyEngine
> key(DNSCryptoKeyEngine::makeFromISCFile(drc
, fname
.c_str()));
2831 dpk
.d_algorithm
= drc
.d_algorithm
;
2833 if(dpk
.d_algorithm
== DNSSECKeeper::RSASHA1NSEC3SHA1
)
2834 dpk
.d_algorithm
= DNSSECKeeper::RSASHA1
;
2838 bool published
=true;
2840 for(unsigned int n
= 3; n
< cmds
.size(); ++n
) {
2841 if(pdns_iequals(cmds
[n
], "ZSK"))
2843 else if(pdns_iequals(cmds
[n
], "KSK"))
2845 else if(pdns_iequals(cmds
[n
], "active"))
2847 else if(pdns_iequals(cmds
[n
], "passive") || pdns_iequals(cmds
[n
], "inactive")) // passive eventually needs to be removed
2849 else if(pdns_iequals(cmds
[n
], "published"))
2851 else if(pdns_iequals(cmds
[n
], "unpublished"))
2854 cerr
<<"Unknown key flag '"<<cmds
[n
]<<"'"<<endl
;
2859 if (!dk
.addKey(DNSName(zone
), dpk
, id
, active
, published
)) {
2860 cerr
<<"Adding key failed, perhaps DNSSEC not enabled in configuration?"<<endl
;
2864 cerr
<<std::to_string(id
)<<"Key was added, but backend does not support returning of key id"<<endl
;
2865 } else if (id
< -1) {
2866 cerr
<<std::to_string(id
)<<"Key was added, but there was a failure while returning the key id"<<endl
;
2868 cout
<<std::to_string(id
)<<endl
;
2871 else if(cmds
[0]=="export-zone-dnskey") {
2872 if(cmds
.size() < 3) {
2873 cerr
<<"Syntax: pdnsutil export-zone-dnskey ZONE KEY-ID"<<endl
;
2877 DNSName
zone(cmds
[1]);
2878 unsigned int id
=pdns_stou(cmds
[2]);
2879 DNSSECPrivateKey dpk
=dk
.getKeyById(zone
, id
);
2880 cout
<< zone
<<" IN DNSKEY "<<dpk
.getDNSKEY().getZoneRepresentation() <<endl
;
2882 else if(cmds
[0] == "generate-zone-key") {
2883 if(cmds
.size() < 2 ) {
2884 cerr
<< "Syntax: pdnsutil generate-zone-key zsk|ksk [rsasha1|rsasha1-nsec3-sha1|rsasha256|rsasha512|ecdsa256|ecdsa384";
2885 #if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBDECAF) || defined(HAVE_LIBCRYPTO_ED25519)
2888 #if defined(HAVE_LIBDECAF) || defined(HAVE_LIBCRYPTO_ED448)
2891 cerr
<< "] [bits]"<<endl
;
2894 // need to get algorithm, bits & ksk or zsk from commandline
2895 bool keyOrZone
=false;
2898 int algorithm
=DNSSECKeeper::ECDSA256
;
2899 for(unsigned int n
=1; n
< cmds
.size(); ++n
) {
2900 if(pdns_iequals(cmds
[n
], "zsk"))
2902 else if(pdns_iequals(cmds
[n
], "ksk"))
2904 else if((tmp_algo
= DNSSECKeeper::shorthand2algorithm(cmds
[n
]))>0) {
2905 algorithm
= tmp_algo
;
2906 } else if(pdns_stou(cmds
[n
]))
2907 bits
= pdns_stou(cmds
[n
]);
2909 cerr
<<"Unknown algorithm, key flag or size '"<<cmds
[n
]<<"'"<<endl
;
2913 cerr
<<"Generating a " << (keyOrZone
? "KSK" : "ZSK")<<" with algorithm = "<<algorithm
<<endl
;
2915 cerr
<<"Requesting specific key size of "<<bits
<<" bits"<<endl
;
2917 DNSSECPrivateKey dspk
;
2918 shared_ptr
<DNSCryptoKeyEngine
> dpk(DNSCryptoKeyEngine::make(algorithm
));
2921 bits
= keyOrZone
? 2048 : 1024;
2923 if(algorithm
== DNSSECKeeper::ECCGOST
|| algorithm
== DNSSECKeeper::ECDSA256
|| algorithm
== DNSSECKeeper::ED25519
)
2925 else if(algorithm
== DNSSECKeeper::ECDSA384
)
2927 else if(algorithm
== DNSSECKeeper::ED448
)
2930 throw runtime_error("Can not guess key size for algorithm "+std::to_string(algorithm
));
2936 dspk
.d_algorithm
= algorithm
;
2937 dspk
.d_flags
= keyOrZone
? 257 : 256;
2939 // print key to stdout
2940 cout
<< "Flags: " << dspk
.d_flags
<< endl
<<
2941 dspk
.getKey()->convertToISC() << endl
;
2942 } else if (cmds
[0]=="generate-tsig-key") {
2943 string usage
= "Syntax: " + cmds
[0] + " name (hmac-md5|hmac-sha1|hmac-sha224|hmac-sha256|hmac-sha384|hmac-sha512)";
2944 if (cmds
.size() < 3) {
2945 cerr
<< usage
<< endl
;
2948 DNSName
name(cmds
[1]);
2949 DNSName
algo(cmds
[2]);
2952 key
= makeTSIGKey(algo
);
2953 } catch(const PDNSException
& e
) {
2954 cerr
<< "Could not create new TSIG key " << name
<< " " << algo
<< ": "<< e
.reason
<< endl
;
2958 UeberBackend
B("default");
2959 if (B
.setTSIGKey(name
, DNSName(algo
), key
)) { // you are feeling bored, put up DNSName(algo) up earlier
2960 cout
<< "Create new TSIG key " << name
<< " " << algo
<< " " << key
<< endl
;
2962 cerr
<< "Failure storing new TSIG key " << name
<< " " << algo
<< " " << key
<< endl
;
2966 } else if (cmds
[0]=="import-tsig-key") {
2967 if (cmds
.size() < 4) {
2968 cerr
<< "Syntax: " << cmds
[0] << " name algorithm key" << endl
;
2971 DNSName
name(cmds
[1]);
2972 string algo
= cmds
[2];
2973 string key
= cmds
[3];
2975 UeberBackend
B("default");
2976 if (B
.setTSIGKey(name
, DNSName(algo
), key
)) {
2977 cout
<< "Imported TSIG key " << name
<< " " << algo
<< endl
;
2979 cerr
<< "Failure importing TSIG key " << name
<< " " << algo
<< endl
;
2983 } else if (cmds
[0]=="delete-tsig-key") {
2984 if (cmds
.size() < 2) {
2985 cerr
<< "Syntax: " << cmds
[0] << " name" << endl
;
2988 DNSName
name(cmds
[1]);
2990 UeberBackend
B("default");
2991 if (B
.deleteTSIGKey(name
)) {
2992 cout
<< "Deleted TSIG key " << name
<< endl
;
2994 cerr
<< "Failure deleting TSIG key " << name
<< endl
;
2998 } else if (cmds
[0]=="list-tsig-keys") {
2999 std::vector
<struct TSIGKey
> keys
;
3000 UeberBackend
B("default");
3001 if (B
.getTSIGKeys(keys
)) {
3002 for(const TSIGKey
&key
: keys
) {
3003 cout
<< key
.name
.toString() << " " << key
.algorithm
.toString() << " " << key
.key
<< endl
;
3007 } else if (cmds
[0]=="activate-tsig-key") {
3009 if (cmds
.size() < 4) {
3010 cerr
<< "Syntax: " << cmds
[0] << " ZONE NAME {master|slave}" << endl
;
3013 DNSName
zname(cmds
[1]);
3014 string name
= cmds
[2];
3015 if (cmds
[3] == "master")
3016 metaKey
= "TSIG-ALLOW-AXFR";
3017 else if (cmds
[3] == "slave")
3018 metaKey
= "AXFR-MASTER-TSIG";
3020 cerr
<< "Invalid parameter '" << cmds
[3] << "', expected master or slave" << endl
;
3023 UeberBackend
B("default");
3024 std::vector
<std::string
> meta
;
3025 if (!B
.getDomainMetadata(zname
, metaKey
, meta
)) {
3026 cerr
<< "Failure enabling TSIG key " << name
<< " for " << zname
<< endl
;
3030 for(std::string tmpname
: meta
) {
3031 if (tmpname
== name
) { found
= true; break; }
3033 if (!found
) meta
.push_back(name
);
3034 if (B
.setDomainMetadata(zname
, metaKey
, meta
)) {
3035 cout
<< "Enabled TSIG key " << name
<< " for " << zname
<< endl
;
3037 cerr
<< "Failure enabling TSIG key " << name
<< " for " << zname
<< endl
;
3041 } else if (cmds
[0]=="deactivate-tsig-key") {
3043 if (cmds
.size() < 4) {
3044 cerr
<< "Syntax: " << cmds
[0] << " ZONE NAME {master|slave}" << endl
;
3047 DNSName
zname(cmds
[1]);
3048 string name
= cmds
[2];
3049 if (cmds
[3] == "master")
3050 metaKey
= "TSIG-ALLOW-AXFR";
3051 else if (cmds
[3] == "slave")
3052 metaKey
= "AXFR-MASTER-TSIG";
3054 cerr
<< "Invalid parameter '" << cmds
[3] << "', expected master or slave" << endl
;
3058 UeberBackend
B("default");
3059 std::vector
<std::string
> meta
;
3060 if (!B
.getDomainMetadata(zname
, metaKey
, meta
)) {
3061 cerr
<< "Failure disabling TSIG key " << name
<< " for " << zname
<< endl
;
3064 std::vector
<std::string
>::iterator iter
= meta
.begin();
3065 for(;iter
!= meta
.end(); ++iter
) if (*iter
== name
) break;
3066 if (iter
!= meta
.end()) meta
.erase(iter
);
3067 if (B
.setDomainMetadata(zname
, metaKey
, meta
)) {
3068 cout
<< "Disabled TSIG key " << name
<< " for " << zname
<< endl
;
3070 cerr
<< "Failure disabling TSIG key " << name
<< " for " << zname
<< endl
;
3074 } else if (cmds
[0]=="get-meta") {
3075 UeberBackend
B("default");
3076 if (cmds
.size() < 2) {
3077 cerr
<< "Syntax: " << cmds
[0] << " zone [kind kind ..]" << endl
;
3080 DNSName
zone(cmds
[1]);
3081 vector
<string
> keys
;
3084 if (!B
.getDomainInfo(zone
, di
)) {
3085 cerr
<< "Invalid zone '" << zone
<< "'" << endl
;
3089 if (cmds
.size() > 2) {
3090 keys
.assign(cmds
.begin() + 2, cmds
.end());
3091 std::cout
<< "Metadata for '" << zone
<< "'" << endl
;
3092 for(const auto& kind
: keys
) {
3093 vector
<string
> meta
;
3095 if (B
.getDomainMetadata(zone
, kind
, meta
)) {
3096 cout
<< kind
<< " = " << boost::join(meta
, ", ") << endl
;
3100 std::map
<std::string
, std::vector
<std::string
> > meta
;
3101 std::cout
<< "Metadata for '" << zone
<< "'" << endl
;
3102 B
.getAllDomainMetadata(zone
, meta
);
3103 for(const auto& each_meta
: meta
) {
3104 cout
<< each_meta
.first
<< " = " << boost::join(each_meta
.second
, ", ") << endl
;
3109 } else if (cmds
[0]=="set-meta" || cmds
[0]=="add-meta") {
3110 if (cmds
.size() < 3) {
3111 cerr
<< "Syntax: " << cmds
[0] << " ZONE KIND [VALUE VALUE ..]" << endl
;
3114 DNSName
zone(cmds
[1]);
3115 string kind
= cmds
[2];
3116 static vector
<string
> multiMetaWhitelist
= {"ALLOW-AXFR-FROM", "ALLOW-DNSUPDATE-FROM",
3117 "ALSO-NOTIFY", "TSIG-ALLOW-AXFR", "TSIG-ALLOW-DNSUPDATE", "GSS-ALLOW-AXFR-PRINCIPAL",
3119 bool clobber
= true;
3120 if (cmds
[0] == "add-meta") {
3122 if (find(multiMetaWhitelist
.begin(), multiMetaWhitelist
.end(), kind
) == multiMetaWhitelist
.end() && kind
.find("X-") != 0) {
3123 cerr
<<"Refusing to add metadata to single-value metadata "<<kind
<<endl
;
3127 vector
<string
> meta(cmds
.begin() + 3, cmds
.end());
3128 return addOrSetMeta(zone
, kind
, meta
, clobber
);
3129 } else if (cmds
[0]=="hsm") {
3131 UeberBackend
B("default");
3132 if (cmds
.size() < 2) {
3133 cerr
<< "Missing sub-command for pdnsutil hsm"<< std::endl
;
3135 } else if (cmds
[1] == "assign") {
3136 DNSCryptoKeyEngine::storvector_t storvect
;
3138 std::vector
<DNSBackend::KeyData
> keys
;
3140 if (cmds
.size() < 9) {
3141 std::cout
<< "Usage: pdnsutil hsm assign ZONE ALGORITHM {ksk|zsk} MODULE TOKEN PIN LABEL (PUBLABEL)" << std::endl
;
3145 DNSName
zone(cmds
[2]);
3148 if (!B
.getDomainInfo(zone
, di
)) {
3149 cerr
<< "Unable to assign module to unknown zone '" << zone
<< "'" << std::endl
;
3153 int algorithm
= DNSSECKeeper::shorthand2algorithm(cmds
[3]);
3155 cerr
<< "Unable to use unknown algorithm '" << cmds
[3] << "'" << std::endl
;
3160 bool keyOrZone
= (cmds
[4] == "ksk" ? true : false);
3161 string module
= cmds
[5];
3162 string slot
= cmds
[6];
3163 string pin
= cmds
[7];
3164 string label
= cmds
[8];
3166 if (cmds
.size() > 9)
3167 pub_label
= cmds
[9];
3171 std::ostringstream iscString
;
3172 iscString
<< "Private-key-format: v1.2" << std::endl
<<
3173 "Algorithm: " << algorithm
<< std::endl
<<
3174 "Engine: " << module
<< std::endl
<<
3175 "Slot: " << slot
<< std::endl
<<
3176 "PIN: " << pin
<< std::endl
<<
3177 "Label: " << label
<< std::endl
<<
3178 "PubLabel: " << pub_label
<< std::endl
;
3180 DNSKEYRecordContent drc
;
3181 DNSSECPrivateKey dpk
;
3182 dpk
.d_flags
= (keyOrZone
? 257 : 256);
3184 shared_ptr
<DNSCryptoKeyEngine
> dke(DNSCryptoKeyEngine::makeFromISCString(drc
, iscString
.str()));
3185 if(!dke
->checkKey()) {
3186 cerr
<< "Invalid DNS Private Key in engine " << module
<< " slot " << slot
<< std::endl
;
3191 // make sure this key isn't being reused.
3192 B
.getDomainKeys(zone
, keys
);
3195 for(DNSBackend::KeyData
& kd
: keys
) {
3196 if (kd
.content
== iscString
.str()) {
3197 // it's this one, I guess...
3204 cerr
<< "You have already assigned this key with ID=" << id
<< std::endl
;
3208 if (!dk
.addKey(zone
, dpk
, id
)) {
3209 cerr
<< "Unable to assign module slot to zone" << std::endl
;
3213 cerr
<< "Module " << module
<< " slot " << slot
<< " assigned to " << zone
<< " with key id " << id
<< endl
;
3216 } else if (cmds
[1] == "create-key") {
3218 if (cmds
.size() < 4) {
3219 cerr
<< "Usage: pdnsutil hsm create-key ZONE KEY-ID [BITS]" << endl
;
3223 DNSName
zone(cmds
[2]);
3227 if (!B
.getDomainInfo(zone
, di
)) {
3228 cerr
<< "Unable to create key for unknown zone '" << zone
<< "'" << std::endl
;
3232 id
= pdns_stou(cmds
[3]);
3233 std::vector
<DNSBackend::KeyData
> keys
;
3234 if (!B
.getDomainKeys(zone
, keys
)) {
3235 cerr
<< "No keys found for zone " << zone
<< std::endl
;
3239 std::shared_ptr
<DNSCryptoKeyEngine
> dke
= nullptr;
3240 // lookup correct key
3241 for(DNSBackend::KeyData
&kd
: keys
) {
3244 DNSKEYRecordContent dkrc
;
3245 dke
= DNSCryptoKeyEngine::makeFromISCString(dkrc
, kd
.content
);
3250 cerr
<< "Could not find key with ID " << id
<< endl
;
3253 if (cmds
.size() > 4) {
3254 bits
= pdns_stou(cmds
[4]);
3257 cerr
<< "Invalid bit size " << bits
<< "given, must be positive integer";
3262 } catch (PDNSException
& e
) {
3263 cerr
<< e
.reason
<< endl
;
3267 cerr
<< "Key of size " << bits
<< " created" << std::endl
;
3271 cerr
<<"PKCS#11 support not enabled"<<endl
;
3274 } else if (cmds
[0] == "b2b-migrate") {
3275 if (cmds
.size() < 3) {
3276 cerr
<<"Usage: b2b-migrate OLD NEW"<<endl
;
3280 DNSBackend
*src
,*tgt
;
3283 for(DNSBackend
*b
: BackendMakers().all()) {
3284 if (b
->getPrefix() == cmds
[1]) src
= b
;
3285 if (b
->getPrefix() == cmds
[2]) tgt
= b
;
3288 cerr
<<"Unknown source backend '"<<cmds
[1]<<"'"<<endl
;
3292 cerr
<<"Unknown target backend '"<<cmds
[2]<<"'"<<endl
;
3296 cout
<<"Moving zone(s) from "<<src
->getPrefix()<<" to "<<tgt
->getPrefix()<<endl
;
3298 vector
<DomainInfo
> domains
;
3300 tgt
->getAllDomains(&domains
, true);
3301 if (domains
.size()>0)
3302 throw PDNSException("Target backend has domain(s), please clean it first");
3304 src
->getAllDomains(&domains
, true);
3306 for(const DomainInfo
& di
: domains
) {
3309 DNSResourceRecord rr
;
3310 cout
<<"Processing '"<<di
.zone
<<"'"<<endl
;
3312 if (!tgt
->createDomain(di
.zone
)) throw PDNSException("Failed to create zone");
3313 if (!tgt
->getDomainInfo(di
.zone
, di_new
)) throw PDNSException("Failed to create zone");
3314 tgt
->setKind(di_new
.zone
, di
.kind
);
3315 tgt
->setAccount(di_new
.zone
,di
.account
);
3318 for(const auto& master
: di
.masters
) {
3322 masters
+= master
.toStringWithPortExcept(53);
3324 tgt
->setMaster(di_new
.zone
, masters
);
3326 if (!src
->list(di
.zone
, di
.id
, true)) throw PDNSException("Failed to list records");
3329 tgt
->startTransaction(di
.zone
, di_new
.id
);
3331 while(src
->get(rr
)) {
3332 rr
.domain_id
= di_new
.id
;
3333 if (!tgt
->feedRecord(rr
, DNSName())) throw PDNSException("Failed to feed record");
3339 if (src
->listComments(di
.id
)) {
3341 while(src
->getComment(c
)) {
3342 c
.domain_id
= di_new
.id
;
3343 tgt
->feedComment(c
);
3349 std::map
<std::string
, std::vector
<std::string
> > meta
;
3350 if (src
->getAllDomainMetadata(di
.zone
, meta
)) {
3351 for (const auto& i
: meta
) {
3352 if (!tgt
->setDomainMetadata(di
.zone
, i
.first
, i
.second
)) throw PDNSException("Failed to feed domain metadata");
3358 // temp var for KeyID
3360 std::vector
<DNSBackend::KeyData
> keys
;
3361 if (src
->getDomainKeys(di
.zone
, keys
)) {
3362 for(const DNSBackend::KeyData
& k
: keys
) {
3363 tgt
->addDomainKey(di
.zone
, k
, keyID
);
3367 tgt
->commitTransaction();
3368 cout
<<"Moved "<<nr
<<" record(s), "<<nc
<<" comment(s), "<<nm
<<" metadata(s) and "<<nk
<<" cryptokey(s)"<<endl
;
3373 std::vector
<struct TSIGKey
> tkeys
;
3374 if (src
->getTSIGKeys(tkeys
)) {
3375 for(auto& tk
: tkeys
) {
3376 if (!tgt
->setTSIGKey(tk
.name
, tk
.algorithm
, tk
.key
)) throw PDNSException("Failed to feed TSIG key");
3380 cout
<<"Moved "<<ntk
<<" TSIG key(s)"<<endl
;
3382 cout
<<"Remember to drop the old backend and run rectify-all-zones"<<endl
;
3385 } else if (cmds
[0] == "backend-cmd") {
3386 if (cmds
.size() < 3) {
3387 cerr
<<"Usage: backend-cmd BACKEND CMD [CMD..]"<<endl
;
3394 for(DNSBackend
*b
: BackendMakers().all()) {
3395 if (b
->getPrefix() == cmds
[1]) db
= b
;
3399 cerr
<<"Unknown backend '"<<cmds
[1]<<"'"<<endl
;
3403 for(auto i
=next(begin(cmds
),2); i
!= end(cmds
); ++i
) {
3404 cerr
<<"== "<<*i
<<endl
;
3405 cout
<<db
->directBackendCmd(*i
);
3410 cerr
<<"Unknown command '"<<cmds
[0] <<"'"<< endl
;
3415 catch(PDNSException
& ae
) {
3416 cerr
<<"Error: "<<ae
.reason
<<endl
;
3419 catch(std::exception
& e
) {
3420 cerr
<<"Error: "<<e
.what()<<endl
;
3425 cerr
<<"Caught an unknown exception"<<endl
;