]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/pdnsutil.cc
add gcc 6.0 support to boost.m4
[thirdparty/pdns.git] / pdns / pdnsutil.cc
CommitLineData
870a0fe4
AT
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#endif
1d211b1b 4#include "dnsseckeeper.hh"
d3151289 5#include "dnssecinfra.hh"
1d211b1b 6#include "statbag.hh"
01fde57c 7#include "base32.hh"
ed3f8559 8#include "base64.hh"
fa8fd4d2 9
1d211b1b 10#include <boost/program_options.hpp>
8daea594 11#include <boost/assign/std/vector.hpp>
451ba512 12#include <boost/assign/list_of.hpp>
20002664
BH
13#include "dnsbackend.hh"
14#include "ueberbackend.hh"
15#include "arguments.hh"
16#include "packetcache.hh"
aa65a832 17#include "zoneparser-tng.hh"
ea937fd4 18#include "signingpipe.hh"
a56bc64d 19#include "dns_random.hh"
a0a6cb58 20#include <fstream>
c2e0881c 21#include <termios.h> //termios, TCSANOW, ECHO, ICANON
22
d4f29089
KM
23#ifdef HAVE_LIBSODIUM
24#include <sodium.h>
25#endif
7581a35b
KM
26#ifdef HAVE_OPENSSL
27#include "opensslsigners.hh"
28#endif
a2b0e9b5
KM
29#ifdef HAVE_SQLITE3
30#include "ssqlite3.hh"
fd9f80de 31#include "bind-dnssec.schema.sqlite3.sql.h"
a2b0e9b5 32#endif
49449751 33
20002664
BH
34StatBag S;
35PacketCache PC;
1d211b1b 36
1d211b1b
BH
37namespace po = boost::program_options;
38po::variables_map g_vm;
39
39a8b5c0 40string s_programname="pdns";
20002664 41
b3ce3dec 42namespace {
b8572438 43 bool g_verbose;
49449751
BH
44}
45
20002664
BH
46ArgvMap &arg()
47{
48 static ArgvMap arg;
49 return arg;
50}
51
1d211b1b
BH
52string humanTime(time_t t)
53{
54 char ret[256];
55 struct tm tm;
56 localtime_r(&t, &tm);
57 strftime(ret, sizeof(ret)-1, "%c", &tm); // %h:%M %Y-%m-%d
58 return ret;
59}
60
e0ad7bb1
PD
61static void algorithm2name(uint8_t algo, string &name) {
62 switch(algo) {
63 case 0:
64 name = "Reserved"; return;
65 case 1:
66 name = "RSAMD5"; return;
67 case 2:
68 name = "DH"; return;
69 case 3:
70 name = "DSA"; return;
71 case 4:
72 name = "ECC"; return;
73 case 5:
74 name = "RSASHA1"; return;
75 case 6:
76 name = "DSA-NSEC3-SHA1"; return;
77 case 7:
78 name = "RSASHA1-NSEC3-SHA1"; return;
79 case 8:
80 name = "RSASHA256"; return;
81 case 9:
82 name = "Reserved"; return;
83 case 10:
84 name = "RSASHA512"; return;
85 case 11:
86 name = "Reserved"; return;
87 case 12:
88 name = "ECC-GOST"; return;
89 case 13:
90 name = "ECDSAP256SHA256"; return;
91 case 14:
92 name = "ECDSAP384SHA384"; return;
45826dd7
KM
93 case 250:
94 name = "ED25519SHA512"; return;
e0ad7bb1
PD
95 case 252:
96 name = "INDIRECT"; return;
97 case 253:
98 name = "PRIVATEDNS"; return;
99 case 254:
100 name = "PRIVATEOID"; return;
101 default:
102 name = "Unallocated/Reserved"; return;
232f0877 103 }
e0ad7bb1
PD
104};
105
36758d25
PD
106static int shorthand2algorithm(const string &algorithm)
107{
108 if (!algorithm.compare("rsamd5")) return 1;
109 if (!algorithm.compare("dh")) return 2;
110 if (!algorithm.compare("dsa")) return 3;
111 if (!algorithm.compare("ecc")) return 4;
112 if (!algorithm.compare("rsasha1")) return 5;
113 if (!algorithm.compare("rsasha256")) return 8;
114 if (!algorithm.compare("rsasha512")) return 10;
115 if (!algorithm.compare("gost")) return 12;
116 if (!algorithm.compare("ecdsa256")) return 13;
117 if (!algorithm.compare("ecdsa384")) return 14;
feafa2f3 118 if (!algorithm.compare("experimental-ed25519")) return 250;
36758d25
PD
119 return -1;
120}
121
7d9dcde0 122void loadMainConfig(const std::string& configdir)
20002664 123{
7d9dcde0 124 ::arg().set("config-dir","Location of configuration directory (pdns.conf)")=configdir;
f2e7d77b 125 ::arg().set("default-ttl","Seconds a result is valid if not set otherwise")="3600";
20002664 126 ::arg().set("launch","Which backends to launch");
6dfa0aa0 127 ::arg().set("dnssec","if we should do dnssec")="true";
4f6cf113 128 ::arg().set("config-name","Name of this virtual configuration - will rename the binary image")=g_vm["config-name"].as<string>();
20002664
BH
129 ::arg().setCmd("help","Provide a helpful message");
130 //::arg().laxParse(argc,argv);
131
132 if(::arg().mustDo("help")) {
ff5ba4f9
WA
133 cout<<"syntax:"<<endl<<endl;
134 cout<<::arg().helpstring(::arg()["help"])<<endl;
135 exit(0);
20002664
BH
136 }
137
138 if(::arg()["config-name"]!="")
139 s_programname+="-"+::arg()["config-name"];
140
141 string configname=::arg()["config-dir"]+"/"+s_programname+".conf";
142 cleanSlashes(configname);
36758d25 143
5209ee17 144 ::arg().set("default-ksk-algorithms","Default KSK algorithms")="";
36758d25 145 ::arg().set("default-ksk-size","Default KSK size (0 means default)")="0";
d113baca 146 ::arg().set("default-zsk-algorithms","Default ZSK algorithms")="ecdsa256";
92bdc094 147 ::arg().set("default-zsk-size","Default ZSK size (0 means default)")="0";
4192773a
KM
148 ::arg().set("default-soa-edit","Default SOA-EDIT value")="";
149 ::arg().set("default-soa-edit-signed","Default SOA-EDIT value for signed zones")="";
b5baefaf 150 ::arg().set("max-ent-entries", "Maximum number of empty non-terminals in a zone")="100000";
0949b8ae 151 ::arg().set("module-dir","Default directory for modules")=PKGLIBDIR;
a56bc64d 152 ::arg().set("entropy-source", "If set, read entropy from this file")="/dev/urandom";
0f310932
AT
153 ::arg().setSwitch("query-logging","Hint backends that queries should be logged")="no";
154 ::arg().set("loglevel","Amount of logging. Higher is more.")="0";
cc8df07f 155 ::arg().setSwitch("direct-dnskey","Fetch DNSKEY RRs from backend during DNSKEY synthesis")="no";
28b66a94 156 ::arg().set("max-nsec3-iterations","Limit the number of NSEC3 hash iterations")="500"; // RFC5155 10.3
bba84134 157 ::arg().set("max-signature-cache-entries", "Maximum number of signatures cache entries")="";
966828ac 158 ::arg().laxFile(configname.c_str());
12a92688 159
38392ad7 160 L.toConsole(Logger::Error); // so we print any errors
20002664 161 BackendMakers().launch(::arg()["launch"]); // vrooooom!
38392ad7 162 L.toConsole((Logger::Urgency)(::arg().asNum("loglevel")));
20002664 163 ::arg().laxFile(configname.c_str());
9abd98d3 164 //cerr<<"Backend: "<<::arg()["launch"]<<", '" << ::arg()["gmysql-dbname"] <<"'" <<endl;
20002664
BH
165
166 S.declare("qsize-q","Number of questions waiting for database attention");
167
168 S.declare("deferred-cache-inserts","Amount of cache inserts that were deferred because of maintenance");
169 S.declare("deferred-cache-lookup","Amount of cache lookups that were deferred because of maintenance");
170
171 S.declare("query-cache-hit","Number of hits on the query cache");
172 S.declare("query-cache-miss","Number of misses on the query cache");
173 ::arg().set("max-cache-entries", "Maximum number of cache entries")="1000000";
174 ::arg().set("recursor","If recursion is desired, IP address of a recursing nameserver")="no";
ec7f535c 175 ::arg().set("recursive-cache-ttl","Seconds to store packets for recursive queries in the PacketCache")="10";
20002664 176 ::arg().set("cache-ttl","Seconds to store packets in the PacketCache")="20";
ec7f535c
PD
177 ::arg().set("negquery-cache-ttl","Seconds to store negative query results in the QueryCache")="60";
178 ::arg().set("query-cache-ttl","Seconds to store query results in the QueryCache")="20";
da6a2926 179 ::arg().set("default-soa-name","name to insert in the SOA record if none set in the backend")="a.misconfigured.powerdns.server";
326a1978 180 ::arg().set("default-soa-mail","mail address to insert in the SOA record if none set in the backend")="";
20002664
BH
181 ::arg().set("soa-refresh-default","Default SOA refresh")="10800";
182 ::arg().set("soa-retry-default","Default SOA retry")="3600";
183 ::arg().set("soa-expire-default","Default SOA expire")="604800";
abc1d928 184 ::arg().set("soa-minimum-ttl","Default SOA minimum ttl")="3600";
12a92688 185
20002664
BH
186 UeberBackend::go();
187}
188
d6c16697 189// irritatingly enough, rectifyZone needs its own ueberbackend and can't therefore benefit from transactions outside its scope
d4904322 190// I think this has to do with interlocking transactions between B and DK, but unsure.
675fa24c 191bool rectifyZone(DNSSECKeeper& dk, const DNSName& zone)
20002664 192{
9bd211e1 193 if(dk.isPresigned(zone)){
675fa24c 194 cerr<<"Rectify presigned zone '"<<zone.toString()<<"' is not allowed/necessary."<<endl;
9bd211e1
KM
195 return false;
196 }
197
ba86110a 198 UeberBackend B("default");
d6c16697 199 bool doTransaction=true; // but see above
20002664 200 SOAData sd;
a279bc18 201
79ba7763 202 if(!B.getSOAUncached(zone, sd)) {
675fa24c 203 cerr<<"No SOA known for '"<<zone.toString()<<"', is such a zone in the database?"<<endl;
032e3906 204 return false;
a279bc18 205 }
81b39e4b 206 sd.db->list(zone, sd.domain_id);
81b39e4b 207
b5baefaf 208 DNSResourceRecord rr;
675fa24c
PD
209 set<DNSName> qnames, nsset, dsnames, insnonterm, delnonterm;
210 map<DNSName,bool> nonterm;
b5baefaf 211 bool doent=true;
a279bc18 212
20002664 213 while(sd.db->get(rr)) {
b5baefaf
PD
214 if (rr.qtype.getCode())
215 {
216 qnames.insert(rr.qname);
4fcd7e9b 217 if(rr.qtype.getCode() == QType::NS && rr.qname != zone)
b5baefaf
PD
218 nsset.insert(rr.qname);
219 if(rr.qtype.getCode() == QType::DS)
220 dsnames.insert(rr.qname);
221 }
222 else
223 if(doent)
224 delnonterm.insert(rr.qname);
81b39e4b 225 }
9abd98d3 226
c3c89361 227 NSEC3PARAMRecordContent ns3pr;
f28f2324 228 bool narrow;
3c873e66 229 bool haveNSEC3=dk.getNSEC3PARAM(zone, &ns3pr, &narrow);
a279bc18 230 bool isOptOut=(haveNSEC3 && ns3pr.d_flags);
ece45ffb
PD
231 if(sd.db->doesDNSSEC())
232 {
a279bc18 233 if(!haveNSEC3)
ece45ffb 234 cerr<<"Adding NSEC ordering information "<<endl;
a279bc18
KM
235 else if(!narrow) {
236 if(!isOptOut)
675fa24c 237 cerr<<"Adding NSEC3 hashed ordering information for '"<<zone.toString()<<"'"<<endl;
a279bc18 238 else
675fa24c 239 cerr<<"Adding NSEC3 opt-out hashed ordering information for '"<<zone.toString()<<"'"<<endl;
a279bc18 240 } else
ece45ffb
PD
241 cerr<<"Erasing NSEC3 ordering since we are narrow, only setting 'auth' fields"<<endl;
242 }
243 else
5911b051 244 cerr<<"Adding empty non-terminals for non-DNSSEC zone"<<endl;
a279bc18 245
d6c16697 246 if(doTransaction)
7da05748 247 sd.db->startTransaction(zone, -1);
a279bc18 248
b5baefaf 249 bool realrr=true;
b5baefaf
PD
250 uint32_t maxent = ::arg().asNum("max-ent-entries");
251
252 dononterm:;
4fcd7e9b 253 for (const auto& qname: qnames)
81b39e4b 254 {
f28f2324 255 bool auth=true;
4fcd7e9b
KM
256 DNSName ordername;
257 auto shorter(qname);
27045410 258
b5baefaf
PD
259 if(realrr) {
260 do {
261 if(nsset.count(shorter)) {
262 auth=false;
263 break;
264 }
675fa24c 265 } while(shorter.chopOff());
b5baefaf 266 }
27045410 267
4fcd7e9b 268 if(haveNSEC3) // NSEC3
27045410 269 {
4fcd7e9b 270 if(!narrow && (realrr || !isOptOut || nonterm.find(qname)->second))
28e2e78e 271 ordername=DNSName(toBase32Hex(hashQNameWithSalt(ns3pr, qname))) + zone;
4fcd7e9b
KM
272 else if(!realrr)
273 auth=false;
b5baefaf 274 }
4fcd7e9b
KM
275 else if (realrr) // NSEC
276 ordername=qname;
277
278 if(g_verbose)
279 cerr<<"'"<<qname.toString()<<"' -> '"<< ordername.toString() <<"'"<<endl;
280 sd.db->updateDNSSECOrderNameAndAuth(sd.domain_id, zone, qname, ordername, auth);
b5baefaf 281
b8adb30d 282 if(realrr)
b5baefaf 283 {
b8adb30d 284 if (dsnames.count(qname))
4fcd7e9b 285 sd.db->updateDNSSECOrderNameAndAuth(sd.domain_id, zone, qname, ordername, true, QType::DS);
b8adb30d 286 if (!auth || nsset.count(qname)) {
4fcd7e9b 287 ordername.clear();
a279bc18 288 if(isOptOut)
4fcd7e9b
KM
289 sd.db->updateDNSSECOrderNameAndAuth(sd.domain_id, zone, qname, ordername, false, QType::NS);
290 sd.db->updateDNSSECOrderNameAndAuth(sd.domain_id, zone, qname, ordername, false, QType::A);
291 sd.db->updateDNSSECOrderNameAndAuth(sd.domain_id, zone, qname, ordername, false, QType::AAAA);
b8adb30d
KM
292 }
293
a279bc18 294 if(doent)
b5baefaf 295 {
b8adb30d 296 shorter=qname;
675fa24c 297 while(shorter!=zone && shorter.chopOff())
b5baefaf 298 {
54c9247e 299 if(!qnames.count(shorter))
b5baefaf 300 {
b8adb30d
KM
301 if(!(maxent))
302 {
675fa24c 303 cerr<<"Zone '"<<zone.toString()<<"' has too many empty non terminals."<<endl;
b8adb30d
KM
304 insnonterm.clear();
305 delnonterm.clear();
306 doent=false;
307 break;
308 }
a279bc18 309
54c9247e
KM
310 if (!delnonterm.count(shorter) && !nonterm.count(shorter))
311 insnonterm.insert(shorter);
312 else
313 delnonterm.erase(shorter);
314
a279bc18 315 if (!nonterm.count(shorter)) {
675fa24c 316 nonterm.insert(pair<DNSName, bool>(shorter, auth));
a279bc18
KM
317 --maxent;
318 } else if (auth)
319 nonterm[shorter]=true;
b5baefaf 320 }
b5baefaf 321 }
27045410 322 }
c3c89361 323 }
20002664 324 }
b5baefaf
PD
325
326 if(realrr)
327 {
328 //cerr<<"Total: "<<nonterm.size()<<" Insert: "<<insnonterm.size()<<" Delete: "<<delnonterm.size()<<endl;
329 if(!insnonterm.empty() || !delnonterm.empty() || !doent)
330 {
331 sd.db->updateEmptyNonTerminals(sd.domain_id, zone, insnonterm, delnonterm, !doent);
332 }
333 if(doent)
334 {
335 realrr=false;
a279bc18 336 qnames.clear();
d4535f5d 337 for(const auto& nt : nonterm){
a279bc18
KM
338 qnames.insert(nt.first);
339 }
b5baefaf
PD
340 goto dononterm;
341 }
342 }
343
d6c16697
BH
344 if(doTransaction)
345 sd.db->commitTransaction();
032e3906
PD
346
347 return true;
20002664
BH
348}
349
a0a6cb58 350void dbBench(const std::string& fname)
351{
352 ::arg().set("query-cache-ttl")="0";
353 ::arg().set("negquery-cache-ttl")="0";
354 UeberBackend B("default");
355
356 vector<string> domains;
357 if(!fname.empty()) {
358 ifstream ifs(fname.c_str());
359 if(!ifs) {
360 cerr<<"Could not open '"<<fname<<"' for reading domain names to query"<<endl;
361 }
362 string line;
363 while(getline(ifs,line)) {
364 trim(line);
365 domains.push_back(line);
366 }
367 }
368 if(domains.empty())
369 domains.push_back("powerdns.com");
370
371 int n=0;
372 DNSResourceRecord rr;
373 DTime dt;
374 dt.set();
375 unsigned int hits=0, misses=0;
376 for(; n < 10000; ++n) {
b873e23a 377 DNSName domain(domains[random() % domains.size()]);
a0a6cb58 378 B.lookup(QType(QType::NS), domain);
379 while(B.get(rr)) {
380 hits++;
381 }
335da0ba 382 B.lookup(QType(QType::A), DNSName(std::to_string(random()))+domain);
a0a6cb58 383 while(B.get(rr)) {
384 }
385 misses++;
386
387 }
388 cout<<0.001*dt.udiff()/n<<" millisecond/lookup"<<endl;
389 cout<<"Retrieved "<<hits<<" records, did "<<misses<<" queries which should have no match"<<endl;
390 cout<<"Packet cache reports: "<<S.read("query-cache-hit")<<" hits (should be 0) and "<<S.read("query-cache-miss") <<" misses"<<endl;
391}
392
1325e8a2
PD
393void rectifyAllZones(DNSSECKeeper &dk)
394{
ba86110a 395 UeberBackend B("default");
1325e8a2
PD
396 vector<DomainInfo> domainInfo;
397
ba86110a 398 B.getAllDomains(&domainInfo);
ff05fd12 399 for(DomainInfo di : domainInfo) {
675fa24c 400 cerr<<"Rectifying "<<di.zone.toString()<<": ";
1325e8a2
PD
401 rectifyZone(dk, di.zone);
402 }
403 cout<<"Rectified "<<domainInfo.size()<<" zones."<<endl;
404}
405
c2e0881c 406int checkZone(DNSSECKeeper &dk, UeberBackend &B, const DNSName& zone, const vector<DNSResourceRecord>* suppliedrecords=0)
5d2e58b0 407{
5d2e58b0 408 SOAData sd;
79ba7763 409 if(!B.getSOAUncached(zone, sd)) {
675fa24c
PD
410 cout<<"[error] No SOA record present, or active, in zone '"<<zone.toString()<<"'"<<endl;
411 cout<<"Checked 0 records of '"<<zone.toString()<<"', 1 errors, 0 warnings."<<endl;
fdc0adaf 412 return 1;
bb0bbee2 413 }
74bf221f
KM
414
415 NSEC3PARAMRecordContent ns3pr;
416 bool narrow = false;
417 bool haveNSEC3 = dk.getNSEC3PARAM(zone, &ns3pr, &narrow);
418 bool isOptOut=(haveNSEC3 && ns3pr.d_flags);
419
420 bool isSecure=dk.isSecuredZone(zone);
bb0bbee2 421 bool presigned=dk.isPresigned(zone);
74bf221f 422
5d2e58b0 423 DNSResourceRecord rr;
8c949c52 424 uint64_t numrecords=0, numerrors=0, numwarnings=0;
f1b672ae 425
3155c04a
PL
426 if (haveNSEC3 && isSecure && zone.wirelength() > 222) {
427 numerrors++;
694ea7ae 428 cout<<"[Error] zone '" << zone.toStringNoDot() << "' 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;
3155c04a 429 }
95ba91f7
KM
430
431 // Check for delegation in parent zone
21a3792f
KM
432 DNSName parent(zone);
433 while(parent.chopOff()) {
95ba91f7
KM
434 SOAData sd_p;
435 if(B.getSOAUncached(parent, sd_p)) {
436 bool ns=false;
437 DNSResourceRecord rr;
438 B.lookup(QType(QType::ANY), zone, NULL, sd_p.domain_id);
439 while(B.get(rr))
440 ns |= (rr.qtype == QType::NS);
441 if (!ns) {
694ea7ae 442 cout<<"[Error] No delegation for zone '"<<zone.toString()<<"' in parent '"<<parent.toString()<<"'"<<endl;
95ba91f7
KM
443 numerrors++;
444 }
445 break;
446 }
447 }
448
449
1e2e9565 450 bool hasNsAtApex = false;
2e5f6225 451 set<DNSName> tlsas, cnames, noncnames, glue, checkglue;
c2e0881c 452 set<string> recordcontents;
bb0bbee2 453 map<string, unsigned int> ttl;
f1b672ae
KM
454
455 ostringstream content;
bb0bbee2 456 pair<map<string, unsigned int>::iterator,bool> ret;
61717a51 457
c2e0881c 458 vector<DNSResourceRecord> records;
459 if(!suppliedrecords) {
460 sd.db->list(zone, sd.domain_id, false);
461 while(sd.db->get(rr)) {
462 records.push_back(rr);
463 }
464 }
465 else
466 records=*suppliedrecords;
95ba91f7 467
c2e0881c 468 for(auto rr : records) { // we modify this
f1b672ae
KM
469 if(!rr.qtype.getCode())
470 continue;
471
a6230867 472 numrecords++;
b191a835 473
2e5f6225
PL
474 if(rr.qtype.getCode() == QType::TLSA)
475 tlsas.insert(rr.qname);
84a0d08c
KM
476 if(rr.qtype.getCode() == QType::SOA) {
477 vector<string>parts;
478 stringtok(parts, rr.content);
479
480 ostringstream o;
481 o<<rr.content;
482 for(int pleft=parts.size(); pleft < 7; ++pleft) {
483 o<<" 0";
484 }
485 rr.content=o.str();
bb0bbee2
KM
486 }
487
77da04f6
KM
488 if(rr.qtype.getCode() == QType::TXT && !rr.content.empty() && rr.content[0]!='"')
489 rr.content = "\""+rr.content+"\"";
490
491 try {
492 shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(rr.qtype.getCode(), 1, rr.content));
493 string tmp=drc->serialize(rr.qname);
f21fc0aa 494 tmp = drc->getZoneRepresentation(true);
0f470360
KM
495 if (rr.qtype.getCode() != QType::AAAA) {
496 if (!pdns_iequals(tmp, rr.content)) {
657c0bf7 497 if(rr.qtype.getCode() == QType::SOA) {
498 tmp = drc->getZoneRepresentation(false);
499 }
500 if(!pdns_iequals(tmp, rr.content)) {
501 cout<<"[Warning] Parsed and original record content are not equal: "<<rr.qname.toString()<<" IN " <<rr.qtype.getName()<< " '" << rr.content<<"' (Content parsed as '"<<tmp<<"')"<<endl;
502 numwarnings++;
503 }
0f470360
KM
504 }
505 } else {
1cac2653
KM
506 struct in6_addr tmpbuf;
507 if (inet_pton(AF_INET6, rr.content.c_str(), &tmpbuf) != 1 || rr.content.find('.') != string::npos) {
675fa24c 508 cout<<"[Warning] Following record is not a valid IPv6 address: "<<rr.qname.toString()<<" IN " <<rr.qtype.getName()<< " '" << rr.content<<"'"<<endl;
0f470360 509 numwarnings++;
1cac2653 510 }
77da04f6
KM
511 }
512 }
513 catch(std::exception& e)
514 {
675fa24c 515 cout<<"[Error] Following record had a problem: "<<rr.qname.toString()<<" IN " <<rr.qtype.getName()<< " " << rr.content<<endl;
77da04f6
KM
516 cout<<"[Error] Error was: "<<e.what()<<endl;
517 numerrors++;
518 continue;
519 }
520
675fa24c 521 if(!rr.qname.isPartOf(zone)) {
e977acfd 522 cout<<"[Error] Record '"<<rr.qname.toString()<<" IN "<<rr.qtype.getName()<<" "<<rr.content<<"' in zone '"<<zone.toString()<<"' is out-of-zone."<<endl;
523 numerrors++;
a6230867
KM
524 continue;
525 }
526
f1b672ae 527 content.str("");
675fa24c 528 content<<rr.qname.toString()<<" "<<rr.qtype.getName()<<" "<<rr.content;
c2e0881c 529 if (recordcontents.count(toLower(content.str()))) {
675fa24c 530 cout<<"[Error] Duplicate record found in rrset: '"<<rr.qname.toString()<<" IN "<<rr.qtype.getName()<<" "<<rr.content<<"'"<<endl;
befcd641
KM
531 numerrors++;
532 continue;
533 } else
c2e0881c 534 recordcontents.insert(toLower(content.str()));
befcd641 535
f1b672ae 536 content.str("");
675fa24c 537 content<<rr.qname.toString()<<" "<<rr.qtype.getName();
63372f34
KM
538 if (rr.qtype.getCode() == QType::RRSIG) {
539 RRSIGRecordContent rrc(rr.content);
540 content<<" ("<<DNSRecordContent::NumberToType(rrc.d_type)<<")";
541 }
bb0bbee2
KM
542 ret = ttl.insert(pair<string, unsigned int>(toLower(content.str()), rr.ttl));
543 if (ret.second == false && ret.first->second != rr.ttl) {
675fa24c 544 cout<<"[Error] TTL mismatch in rrset: '"<<rr.qname.toString()<<" IN " <<rr.qtype.getName()<<" "<<rr.content<<"' ("<<ret.first->second<<" != "<<rr.ttl<<")"<<endl;
f1b672ae
KM
545 numerrors++;
546 continue;
547 }
548
675fa24c
PD
549 if (isSecure && isOptOut && (rr.qname.countLabels() && rr.qname.getRawLabels()[0] == "*")) {
550 cout<<"[Warning] wildcard record '"<<rr.qname.toString()<<" IN " <<rr.qtype.getName()<<" "<<rr.content<<"' is insecure"<<endl;
fd5076c8 551 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.toString()<<endl;
74bf221f
KM
552 numwarnings++;
553 }
554
675fa24c 555 if(rr.qname==zone) {
1e2e9565
KM
556 if (rr.qtype.getCode() == QType::NS) {
557 hasNsAtApex=true;
558 } else if (rr.qtype.getCode() == QType::DS) {
675fa24c 559 cout<<"[Warning] DS at apex in zone '"<<zone.toString()<<"', should not be here."<<endl;
1e2e9565
KM
560 numwarnings++;
561 }
562 } else {
563 if (rr.qtype.getCode() == QType::SOA) {
675fa24c 564 cout<<"[Error] SOA record not at apex '"<<rr.qname.toString()<<" IN "<<rr.qtype.getName()<<" "<<rr.content<<"' in zone '"<<zone.toString()<<"'"<<endl;
1e2e9565
KM
565 numerrors++;
566 continue;
567 } else if (rr.qtype.getCode() == QType::DNSKEY) {
675fa24c 568 cout<<"[Warning] DNSKEY record not at apex '"<<rr.qname.toString()<<" IN "<<rr.qtype.getName()<<" "<<rr.content<<"' in zone '"<<zone.toString()<<"', should not be here."<<endl;
1e2e9565 569 numwarnings++;
21a3792f 570 } else if (rr.qtype.getCode() == QType::NS && DNSName(rr.content).isPartOf(rr.qname)) {
b873e23a 571 checkglue.insert(DNSName(toLower(rr.content)));
95ba91f7 572 } else if (rr.qtype.getCode() == QType::A || rr.qtype.getCode() == QType::AAAA) {
b873e23a 573 glue.insert(rr.qname);
1e2e9565
KM
574 }
575 }
576
61717a51 577 if (rr.qtype.getCode() == QType::CNAME) {
675fa24c
PD
578 if (!cnames.count(rr.qname))
579 cnames.insert(rr.qname);
c90d822b 580 else {
675fa24c 581 cout<<"[Error] Duplicate CNAME found at '"<<rr.qname.toString()<<"'"<<endl;
ca57aa98
KM
582 numerrors++;
583 continue;
584 }
c90d822b
KM
585 } else {
586 if (rr.qtype.getCode() == QType::RRSIG) {
3aa5b00f 587 if(!presigned) {
675fa24c 588 cout<<"[Error] RRSIG found at '"<<rr.qname.toString()<<"' in non-presigned zone. These do not belong in the database."<<endl;
c90d822b 589 numerrors++;
befcd641 590 continue;
c90d822b
KM
591 }
592 } else
675fa24c 593 noncnames.insert(rr.qname);
61717a51
PD
594 }
595
0aabca97
PD
596 if(rr.qtype.getCode() == QType::NSEC || rr.qtype.getCode() == QType::NSEC3)
597 {
675fa24c 598 cout<<"[Error] NSEC or NSEC3 found at '"<<rr.qname.toString()<<"'. These do not belong in the database."<<endl;
0aabca97
PD
599 numerrors++;
600 continue;
601 }
602
0a0f82c8 603 if(!presigned && rr.qtype.getCode() == QType::DNSKEY)
12a92688 604 {
cc8df07f 605 if(::arg().mustDo("direct-dnskey"))
12a92688 606 {
0a0f82c8 607 if(rr.ttl != sd.default_ttl)
12a92688 608 {
675fa24c 609 cout<<"[Warning] DNSKEY TTL of "<<rr.ttl<<" at '"<<rr.qname.toString()<<"' differs from SOA minimum of "<<sd.default_ttl<<endl;
c90d822b 610 numwarnings++;
12a92688
PD
611 }
612 }
0a0f82c8
KM
613 else
614 {
675fa24c 615 cout<<"[Warning] DNSKEY at '"<<rr.qname.toString()<<"' in non-presigned zone will mostly be ignored and can cause problems."<<endl;
0a0f82c8
KM
616 numwarnings++;
617 }
12a92688
PD
618 }
619
675fa24c
PD
620 // if (rr.qname[rr.qname.size()-1] == '.') {
621 // cout<<"[Error] Record '"<<rr.qname.toString()<<"' has a trailing dot. PowerDNS will ignore this record!"<<endl;
622 // numerrors++;
623 // }
bb0bbee2 624
165e5695 625 if ( (rr.qtype.getCode() == QType::NS || rr.qtype.getCode() == QType::SRV || rr.qtype.getCode() == QType::MX || rr.qtype.getCode() == QType::CNAME || rr.qtype.getCode() == QType::DNAME) &&
8c949c52 626 rr.content[rr.content.size()-1] == '.') {
675fa24c 627 cout<<"[Warning] The record "<<rr.qname.toString()<<" with type "<<rr.qtype.getName()<<" has a trailing dot in the content ("<<rr.content<<"). Your backend might not work well with this."<<endl;
8c949c52
PD
628 numwarnings++;
629 }
630
27045410 631 if(rr.auth == 0 && rr.qtype.getCode()!=QType::NS && rr.qtype.getCode()!=QType::A && rr.qtype.getCode()!=QType::AAAA)
7ddd79a7 632 {
fd5076c8 633 cout<<"[Error] Following record is auth=0, run pdnsutil rectify-zone?: "<<rr.qname.toString()<<" IN " <<rr.qtype.getName()<< " " << rr.content<<endl;
5e42374c 634 numerrors++;
7ddd79a7 635 }
035297ad 636 }
61717a51 637
675fa24c
PD
638 for(auto &i: cnames) {
639 if (noncnames.find(i) != noncnames.end()) {
640 cout<<"[Error] CNAME "<<i.toString()<<" found, but other records with same label exist."<<endl;
61717a51
PD
641 numerrors++;
642 }
643 }
644
2e5f6225
PL
645 for(const auto &i: tlsas) {
646 DNSName name = DNSName(i);
647 name.trimToLabels(name.getRawLabels().size()-2);
648 if (cnames.find(name) == cnames.end() && noncnames.find(name) == noncnames.end()) {
649 // No specific record for the name in the TLSA record exists, this
650 // is already worth emitting a warning. Let's see if a wildcard exist.
651 cout<<"[Warning] ";
652 DNSName wcname(name);
653 wcname.chopOff();
654 wcname.prependRawLabel("*");
655 if (cnames.find(wcname) != cnames.end() || noncnames.find(wcname) != noncnames.end()) {
656 cout<<"A wildcard record exist for '"<<wcname.toString()<<"' and a TLSA record for '"<<i.toString()<<"'.";
657 } else {
658 cout<<"No record for '"<<name.toString()<<"' exists, but a TLSA record for '"<<i.toString()<<"' does.";
659 }
660 numwarnings++;
661 cout<<" A query for '"<<name.toString()<<"' will yield an empty response. This is most likely a mistake, please create records for '"<<name.toString()<<"'."<<endl;
662 }
663 }
664
1e2e9565 665 if(!hasNsAtApex) {
675fa24c 666 cout<<"[Error] No NS record at zone apex in zone '"<<zone.toString()<<"'"<<endl;
1e2e9565
KM
667 numerrors++;
668 }
669
95ba91f7
KM
670 for(const auto &qname : checkglue) {
671 if (!glue.count(qname)) {
694ea7ae 672 cout<<"[Warning] Missing glue for '"<<qname.toString()<<"' in zone '"<<zone.toString()<<"'"<<endl;
13a9aa3e 673 numwarnings++;
95ba91f7
KM
674 }
675 }
676
675fa24c 677 cout<<"Checked "<<numrecords<<" records of '"<<zone.toString()<<"', "<<numerrors<<" errors, "<<numwarnings<<" warnings."<<endl;
d6ddb28a
PL
678 if(!numerrors)
679 return 0;
680 return 1;
5d2e58b0
BH
681}
682
d6ddb28a 683int checkAllZones(DNSSECKeeper &dk, bool exitOnError)
1325e8a2 684{
ba86110a 685 UeberBackend B("default");
1325e8a2
PD
686 vector<DomainInfo> domainInfo;
687
fdc0adaf 688 B.getAllDomains(&domainInfo, true);
1325e8a2 689 int errors=0;
d6ddb28a
PL
690 for(auto di : domainInfo) {
691 if (checkZone(dk, B, di.zone) > 0) {
692 errors++;
693 if(exitOnError)
694 return 1;
695 }
1325e8a2
PD
696 }
697 cout<<"Checked "<<domainInfo.size()<<" zones, "<<errors<<" had errors."<<endl;
d6ddb28a
PL
698 if(!errors)
699 return 0;
700 return 1;
1325e8a2
PD
701}
702
675fa24c 703int increaseSerial(const DNSName& zone, DNSSECKeeper &dk)
04576eed
RA
704{
705 UeberBackend B("default");
706 SOAData sd;
79ba7763 707 if(!B.getSOAUncached(zone, sd)) {
694ea7ae 708 cerr<<"No SOA for zone '"<<zone.toString()<<"'"<<endl;
04576eed
RA
709 return -1;
710 }
4192773a
KM
711
712 if (dk.isPresigned(zone)) {
713 cerr<<"Serial increase of presigned zone '"<<zone<<"' is not allowed."<<endl;
714 return -1;
715 }
04576eed
RA
716
717 string soaEditKind;
4192773a 718 dk.getSoaEdit(zone, soaEditKind);
04576eed
RA
719
720 sd.db->lookup(QType(QType::SOA), zone);
721 vector<DNSResourceRecord> rrs;
722 DNSResourceRecord rr;
723 while (sd.db->get(rr)) {
724 if (rr.qtype.getCode() == QType::SOA)
725 rrs.push_back(rr);
726 }
727
728 if (rrs.size() > 1) {
675fa24c 729 cerr<<rrs.size()<<" SOA records found for "<<zone.toString()<<"!"<<endl;
04576eed
RA
730 return -1;
731 }
732 if (rrs.size() < 1) {
675fa24c 733 cerr<<zone.toString()<<" not found!"<<endl;
04576eed
RA
734 }
735
63347c6c 736 if (soaEditKind.empty()) {
04576eed 737 sd.serial++;
63347c6c
SB
738 }
739 else if(pdns_iequals(soaEditKind,"INCREMENT-WEEKS")) {
740 sd.serial++;
741 }
742 else if(pdns_iequals(soaEditKind,"INCEPTION-INCREMENT")) {
743 uint32_t today_serial = localtime_format_YYYYMMDDSS(time(NULL), 1);
744
745 if (sd.serial < today_serial) {
746 sd.serial = today_serial;
747 }
748 else {
749 sd.serial++;
750 }
751 }
752 else {
9f44333a 753 sd.serial = calculateEditSOA(sd, soaEditKind) + 1;
63347c6c 754 }
04576eed
RA
755 rrs[0].content = serializeSOAData(sd);
756
7da05748 757 sd.db->startTransaction(zone, -1);
0881012f 758
04576eed 759 if (! sd.db->replaceRRSet(sd.domain_id, zone, rr.qtype, rrs)) {
0881012f 760 sd.db->abortTransaction();
04576eed
RA
761 cerr<<"Backend did not replace SOA record. Backend might not support this operation."<<endl;
762 return -1;
763 }
481350e4
AT
764
765 if (sd.db->doesDNSSEC()) {
766 NSEC3PARAMRecordContent ns3pr;
767 bool narrow;
768 bool haveNSEC3=dk.getNSEC3PARAM(zone, &ns3pr, &narrow);
769
4fcd7e9b
KM
770 DNSName ordername;
771 if(haveNSEC3) {
772 if(!narrow)
28e2e78e 773 ordername=DNSName(toBase32Hex(hashQNameWithSalt(ns3pr, zone))) + zone;
4fcd7e9b
KM
774 } else
775 ordername=zone;
776 if(g_verbose)
777 cerr<<"'"<<rrs[0].qname.toString()<<"' -> '"<< ordername.toString() <<"'"<<endl;
778 sd.db->updateDNSSECOrderNameAndAuth(sd.domain_id, zone, rrs[0].qname, ordername, true);
481350e4
AT
779 }
780
0881012f
AT
781 sd.db->commitTransaction();
782
675fa24c 783 cout<<"SOA serial for zone "<<zone.toString()<<" set to "<<sd.serial<<endl;
04576eed
RA
784 return 0;
785}
786
675fa24c 787int deleteZone(const DNSName &zone) {
51f6bca1
RA
788 UeberBackend B;
789 DomainInfo di;
790 if (! B.getDomainInfo(zone, di)) {
675fa24c 791 cerr<<"Domain '"<<zone.toString()<<"' not found!"<<endl;
51f6bca1
RA
792 return 1;
793 }
794
795 if(di.backend->deleteDomain(zone))
796 return 0;
797
675fa24c 798 cerr<<"Failed to delete domain '"<<zone.toString()<<"'"<<endl;;
51f6bca1
RA
799 return 1;
800}
801
597b9b3e
PL
802void listKey(DomainInfo const &di, DNSSECKeeper& dk, bool printHeader = true) {
803 if (printHeader) {
804 cout<<"Zone Type Size Algorithm ID Location Keytag"<<endl;
805 cout<<"----------------------------------------------------------------------------------"<<endl;
806 }
807 unsigned int spacelen = 0;
808 for (auto const &key : dk.getKeys(di.zone)) {
809 cout<<di.zone.toStringNoDot();
810 if (di.zone.toStringNoDot().length() > 29)
811 cout<<endl<<string(30, ' ');
812 else
813 cout<<string(30 - di.zone.toStringNoDot().length(), ' ');
814
815 cout<<(key.second.keyOrZone ? "KSK" : "ZSK")<<" ";
816
335da0ba 817 spacelen = (std::to_string(key.first.getKey()->getBits()).length() >= 8) ? 1 : 8 - std::to_string(key.first.getKey()->getBits()).length();
597b9b3e
PL
818 if (key.first.getKey()->getBits() < 1) {
819 cout<<"invalid "<<endl;
820 continue;
821 } else {
822 cout<<key.first.getKey()->getBits()<<string(spacelen, ' ');
823 }
824
825 string algname;
826 algorithm2name(key.first.d_algorithm, algname);
827 spacelen = (algname.length() >= 13) ? 1 : 13 - algname.length();
828 cout<<algname<<string(spacelen, ' ');
829
335da0ba 830 spacelen = (std::to_string(key.second.id).length() > 5) ? 1 : 5 - std::to_string(key.second.id).length();
597b9b3e
PL
831 cout<<key.second.id<<string(spacelen, ' ');
832
833#ifdef HAVE_P11KIT1
834 auto stormap = key.first.getKey()->convertToISCVector();
835 string engine, slot, label = "";
836 for (auto const &elem : stormap) {
837 //cout<<elem.first<<" "<<elem.second<<endl;
838 if (elem.first == "Engine")
839 engine = elem.second;
840 if (elem.first == "Slot")
841 slot = elem.second;
842 if (elem.first == "Label")
843 label = elem.second;
844 }
845 if (engine.empty() || slot.empty()){
846 cout<<"cryptokeys ";
847 } else {
848 spacelen = (engine.length()+slot.length()+label.length()+2 >= 12) ? 1 : 12 - engine.length()-slot.length()-label.length()-2;
849 cout<<engine<<","<<slot<<","<<label<<string(spacelen, ' ');
850 }
851#else
852 cout<<"cryptokeys ";
853#endif
854 cout<<key.first.getDNSKEY().getTag()<<endl;
855 }
856}
857
858bool listKeys(const string &zname, DNSSECKeeper& dk){
859 UeberBackend B("default");
860
861 if (zname != "all") {
862 DomainInfo di;
863 if(!B.getDomainInfo(DNSName(zname), di)) {
864 cerr << "Zone "<<zname<<" not found."<<endl;
865 return false;
866 }
867 listKey(di, dk);
868 } else {
869 vector<DomainInfo> domainInfo;
870 B.getAllDomains(&domainInfo);
871 bool printHeader = true;
872 for (auto const di : domainInfo) {
873 listKey(di, dk, printHeader);
874 printHeader = false;
875 }
876 }
877 return true;
878}
879
675fa24c 880int listZone(const DNSName &zone) {
c9865bc5 881 UeberBackend B;
882 DomainInfo di;
883
884 if (! B.getDomainInfo(zone, di)) {
675fa24c 885 cerr<<"Domain '"<<zone.toString()<<"' not found!"<<endl;
c9865bc5 886 return 1;
887 }
888 di.backend->list(zone, di.id);
889 DNSResourceRecord rr;
890 while(di.backend->get(rr)) {
891 if(rr.qtype.getCode()) {
892 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] != '.')
893 rr.content.append(1, '.');
894
36b32ec8 895 cout<<rr.qname.toString()<<"\t"<<rr.ttl<<"\tIN\t"<<rr.qtype.getName()<<"\t"<<rr.content<<endl;
c9865bc5 896 }
897 }
898 return 0;
899}
900
c2e0881c 901// lovingly copied from http://stackoverflow.com/questions/1798511/how-to-avoid-press-enter-with-any-getchar
902int read1char(){
903 int c;
904 static struct termios oldt, newt;
905
906 /*tcgetattr gets the parameters of the current terminal
907 STDIN_FILENO will tell tcgetattr that it should write the settings
908 of stdin to oldt*/
909 tcgetattr( STDIN_FILENO, &oldt);
910 /*now the settings will be copied*/
911 newt = oldt;
912
913 /*ICANON normally takes care that one line at a time will be processed
914 that means it will return if it sees a "\n" or an EOF or an EOL*/
915 newt.c_lflag &= ~(ICANON);
916
917 /*Those new settings will be set to STDIN
918 TCSANOW tells tcsetattr to change attributes immediately. */
919 tcsetattr( STDIN_FILENO, TCSANOW, &newt);
920
921 c=getchar();
922
923 /*restore the old settings*/
924 tcsetattr( STDIN_FILENO, TCSANOW, &oldt);
925
926 return c;
927}
928
929
930int editZone(DNSSECKeeper& dk, const DNSName &zone) {
931 UeberBackend B;
932 DomainInfo di;
933
934 if (! B.getDomainInfo(zone, di)) {
935 cerr<<"Domain '"<<zone.toString()<<"' not found!"<<endl;
936 return 1;
937 }
938 vector<DNSRecord> pre, post;
939 char tmpnam[]="/tmp/pdnsutil-XXXXXX";
940 int tmpfd=mkstemp(tmpnam);
941 if(tmpfd < 0)
942 unixDie("Making temporary filename in "+string(tmpnam));
943 struct deleteme {
944 ~deleteme() { unlink(d_name.c_str()); }
945 deleteme(string name) : d_name(name) {}
946 string d_name;
947 } dm(tmpnam);
948
949 bool first=true;
950 vector<DNSResourceRecord> checkrr;
951 int gotoline=0;
952 string editor="editor";
953 if(auto e=getenv("EDITOR")) // <3
954 editor=e;
955 string cmdline;
956 editAgain:;
957 di.backend->list(zone, di.id);
958 pre.clear(); post.clear();
959 DNSResourceRecord rr;
960 {
6945584f 961 if(tmpfd < 0 && (tmpfd=open(tmpnam, O_WRONLY | O_TRUNC, 0600)) < 0)
c2e0881c 962 unixDie("Error reopening temporary file "+string(tmpnam));
21dd5918 963 string header("; Warning - every name in this file is ABSOLUTE!\n$ORIGIN .\n");
c2e0881c 964 if(write(tmpfd, header.c_str(), header.length()) < 0)
965 unixDie("Writing zone to temporary file");
966 while(di.backend->get(rr)) {
6945584f 967 if(!rr.qtype.getCode())
968 continue;
c2e0881c 969 DNSRecord dr(rr);
970 pre.push_back(dr);
971 ostringstream os;
972 os<<dr.d_name<<"\t"<<dr.d_ttl<<"\tIN\t"<<DNSRecordContent::NumberToType(dr.d_type)<<"\t"<<dr.d_content->getZoneRepresentation(true)<<endl;
973 if(write(tmpfd, os.str().c_str(), os.str().length()) < 0)
974 unixDie("Writing zone to temporary file");
975 }
976 sort(pre.begin(), pre.end());
977 close(tmpfd);
978 tmpfd=-1;
979 }
980 editMore:;
981 struct stat statbefore, statafter;
982 stat(tmpnam,&statbefore);
983 cmdline=editor+" ";
984 if(gotoline > 0)
985 cmdline+="+"+std::to_string(gotoline)+" ";
986 cmdline += tmpnam;
21dd5918 987 int err=system(cmdline.c_str());
988 if(err) {
989 unixDie("Editing file with: '"+cmdline+"', perhaps set EDITOR variable");
c2e0881c 990 }
991 cmdline.clear();
992 stat(tmpnam,&statafter);
993 if(first && statbefore.st_ctime == statafter.st_ctime) {
994 cout<<"No change to file"<<endl;
995 return(EXIT_SUCCESS);
996 }
997 first=false;
998 ZoneParserTNG zpt(tmpnam, DNSName("."));
999 map<pair<DNSName,uint16_t>, vector<DNSRecord> > grouped;
1000 while(zpt.get(rr)) {
1001 try {
1002 DNSRecord dr(rr);
1003 post.push_back(dr);
1004 grouped[{dr.d_name,dr.d_type}].push_back(dr);
1005 }
1006 catch(std::exception& e) {
1007 cerr<<"Problem "<<e.what()<<" "<<zpt.getLineOfFile()<<endl;
1008 auto fnum = zpt.getLineNumAndFile();
1009 gotoline = fnum.second;
1010 goto reAsk;
1011 }
1012 }
1013 sort(post.begin(), post.end());
1014 checkrr.clear();
1015
1016 for(const DNSRecord& rr : post) {
1017 DNSResourceRecord drr(rr);
1018 drr.domain_id = di.id;
1019 checkrr.push_back(drr);
1020 }
1021 if(checkZone(dk, B, zone, &checkrr)) {
1022 reAsk:;
1023 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;
1024 int c=read1char();
1025 cerr<<"\n";
6945584f 1026 if(c!='a')
1027 post.clear();
c2e0881c 1028 if(c=='e')
1029 goto editMore;
1030 else if(c=='r')
1031 goto editAgain;
1032 else if(c=='q')
1033 return EXIT_FAILURE;
1034 else if(c!='a')
1035 goto reAsk;
1036 }
1037
1038 cout<<"Detected the following changes:\n"<<endl;
1039
1040 vector<DNSRecord> diff;
1041 set<pair<DNSName,uint16_t>> changed;
1042 set_difference(pre.cbegin(), pre.cend(), post.cbegin(), post.cend(), back_inserter(diff));
1043 for(const auto& d : diff) {
1044 changed.insert({d.d_name,d.d_type});
1045 cout<<'-'<< d.d_name <<" "<<d.d_ttl<<" IN "<<DNSRecordContent::NumberToType(d.d_type)<<" "<<d.d_content->getZoneRepresentation(true)<<endl;
1046 }
1047 diff.clear();
1048 set_difference(post.cbegin(), post.cend(), pre.cbegin(), pre.cend(), back_inserter(diff));
1049 for(const auto& d : diff) {
1050 changed.insert({d.d_name,d.d_type});
1051 cout<<'+'<< d.d_name <<" "<<d.d_ttl<<" IN "<<DNSRecordContent::NumberToType(d.d_type)<<" "<<d.d_content->getZoneRepresentation(true)<<endl;
1052 }
1053
1054 reAsk2:;
6945584f 1055 cout<<"\n";
1056 if(changed.empty())
1057 cout<<"No changes to apply, ";
1058 else
1059 cout<<"(a)pply these changes, ";
1060 cout<<"(e)dit again, (r)etry with original zone, (q)uit: ";
c2e0881c 1061 int c=read1char();
1062 post.clear();
1063 cerr<<'\n';
1064 if(c=='q')
1065 return(EXIT_SUCCESS);
1066 else if(c=='e')
1067 goto editMore;
1068 else if(c=='r')
1069 goto editAgain;
6945584f 1070 else if(changed.empty() || c!='a')
c2e0881c 1071 goto reAsk2;
1072
1073 for(const auto& c : changed) {
1074 vector<DNSResourceRecord> vrr;
1075 for(const DNSRecord& rr : grouped[c]) {
1076 DNSResourceRecord drr(rr);
1077 drr.domain_id = di.id;
1078 vrr.push_back(drr);
1079 }
1080 di.backend->replaceRRSet(di.id, c.first, QType(c.second), vrr);
1081 }
6945584f 1082 rectifyZone(dk, zone);
c2e0881c 1083 return 0;
1084}
1085
1086
675fa24c 1087int loadZone(DNSName zone, const string& fname) {
c9865bc5 1088 UeberBackend B;
1089 DomainInfo di;
1090
1091 if (B.getDomainInfo(zone, di)) {
675fa24c 1092 cerr<<"Domain '"<<zone.toString()<<"' exists already, replacing contents"<<endl;
c9865bc5 1093 }
1094 else {
675fa24c 1095 cerr<<"Creating '"<<zone.toString()<<"'"<<endl;
c9865bc5 1096 B.createDomain(zone);
1097
1098 if(!B.getDomainInfo(zone, di)) {
6b32c5e8 1099 cerr<<"Domain '"<<zone.toString()<<"' was not created - perhaps backend ("<<::arg()["launch"]<<") does not support storing new zones."<<endl;
c9865bc5 1100 return 1;
1101 }
1102 }
1103 DNSBackend* db = di.backend;
1104 ZoneParserTNG zpt(fname, zone);
1105
1106 DNSResourceRecord rr;
1107 if(!db->startTransaction(zone, di.id)) {
675fa24c 1108 cerr<<"Unable to start transaction for load of zone '"<<zone.toString()<<"'"<<endl;
c9865bc5 1109 return 1;
1110 }
1111 rr.domain_id=di.id;
1112 while(zpt.get(rr)) {
675fa24c
PD
1113 if(!rr.qname.isPartOf(zone) && rr.qname!=zone) {
1114 cerr<<"File contains record named '"<<rr.qname.toString()<<"' which is not part of zone '"<<zone.toString()<<"'"<<endl;
c9865bc5 1115 return 1;
1116 }
c9865bc5 1117 db->feedRecord(rr);
1118 }
1119 db->commitTransaction();
1120 return 0;
1121}
1122
66d34461 1123int createZone(const DNSName &zone, const DNSName& nsname) {
c9865bc5 1124 UeberBackend B;
1125 DomainInfo di;
1126 if (B.getDomainInfo(zone, di)) {
675fa24c 1127 cerr<<"Domain '"<<zone.toString()<<"' exists already"<<endl;
c9865bc5 1128 return 1;
1129 }
66d34461 1130 cerr<<"Creating empty zone '"<<zone.toString()<<"'"<<endl;
c9865bc5 1131 B.createDomain(zone);
c9865bc5 1132 if(!B.getDomainInfo(zone, di)) {
675fa24c 1133 cerr<<"Domain '"<<zone.toString()<<"' was not created!"<<endl;
c9865bc5 1134 return 1;
1135 }
8e1864cc
CH
1136
1137 DNSResourceRecord rr;
1138 rr.qname = zone;
1139 rr.auth = 1;
1140 rr.ttl = ::arg().asNum("default-ttl");
1141 rr.qtype = "SOA";
66d34461 1142
8e1864cc 1143 string soa = (boost::format("%s %s 1")
66d34461 1144 % (nsname.empty() ? ::arg()["default-soa-name"] : nsname.toString())
1145 % (::arg().isEmpty("default-soa-mail") ? (DNSName("hostmaster.") + zone).toString() : ::arg()["default-soa-mail"])
8e1864cc
CH
1146 ).str();
1147 SOAData sd;
1148 fillSOAData(soa, sd); // fills out default values for us
1149 rr.content = DNSRecordContent::mastermake(rr.qtype.getCode(), 1, serializeSOAData(sd))->getZoneRepresentation(true);
1150 rr.domain_id = di.id;
1151 di.backend->startTransaction(zone, di.id);
1152 di.backend->feedRecord(rr);
66d34461 1153 if(!nsname.empty()) {
1154 cout<<"Also adding one NS record"<<endl;
1155 rr.qtype=QType::NS;
1156 rr.content=nsname.toString();
1157 di.backend->feedRecord(rr);
1158 }
1159
8e1864cc
CH
1160 di.backend->commitTransaction();
1161
c9865bc5 1162 return 1;
1163}
1164
e85a76ad 1165int createSlaveZone(const vector<string>& cmds) {
1166 UeberBackend B;
1167 DomainInfo di;
1168 DNSName zone(cmds[1]);
1169 if (B.getDomainInfo(zone, di)) {
1170 cerr<<"Domain '"<<zone.toString()<<"' exists already"<<endl;
1171 return 1;
1172 }
a3e3bef7 1173 ComboAddress master(cmds[2], 53);
1174 cerr<<"Creating slave zone '"<<zone.toString()<<"', master is "<<master.toStringWithPort()<<endl;
e85a76ad 1175 B.createDomain(zone);
1176 if(!B.getDomainInfo(zone, di)) {
1177 cerr<<"Domain '"<<zone.toString()<<"' was not created!"<<endl;
1178 return 1;
1179 }
1180 di.backend->setKind(zone, DomainInfo::Slave);
a3e3bef7 1181 di.backend->setMaster(zone, master.toStringWithPort());
e85a76ad 1182 return EXIT_SUCCESS;
1183}
1184
66d34461 1185// add-record ZONE name type [ttl] "content" ["content"]
1186int addOrReplaceRecord(bool addOrReplace, const vector<string>& cmds) {
1187 DNSResourceRecord rr;
1188 vector<DNSResourceRecord> newrrs;
1189 DNSName zone(cmds[1]);
1190 DNSName name;
1191 if(cmds[2]=="@")
1192 name=zone;
1193 else
1194 name=DNSName(cmds[2])+zone;
1195
1196 rr.qtype = DNSRecordContent::TypeToNumber(cmds[3]);
1197 rr.ttl = ::arg().asNum("default-ttl");
1198
1199 UeberBackend B;
1200 DomainInfo di;
1201
1202 if(!B.getDomainInfo(zone, di)) {
1203 cerr<<"Domain '"<<zone<<"' does not exist"<<endl;
1204 return 1;
1205 }
1206 rr.auth = 1;
1207 rr.domain_id = di.id;
1208 rr.qname = name;
c07a0c6d 1209 DNSResourceRecord oldrr;
66d34461 1210 if(addOrReplace) { // the 'add' case
1211 B.lookup(rr.qtype, rr.qname, 0, di.id);
c07a0c6d 1212
66d34461 1213 while(B.get(oldrr))
1214 newrrs.push_back(oldrr);
1215 }
1216
1217 unsigned int contentStart = 4;
1218 if(cmds.size() > 5) {
1219 rr.ttl=atoi(cmds[4].c_str());
1220 if(std::to_string(rr.ttl)==cmds[4]) {
1221 contentStart++;
1222 }
1223 else rr.ttl = ::arg().asNum("default-ttl");
1224 }
1225
66d34461 1226 B.lookup(QType(QType::ANY), rr.qname, 0, di.id);
1227 bool found=false;
1228 if(rr.qtype.getCode() == QType::CNAME) { // this will save us SO many questions
1229
c07a0c6d 1230 while(B.get(oldrr)) {
1231 if(addOrReplace || oldrr.qtype.getCode() != QType::CNAME) // the replace case is ok if we replace one CNAME by the other
66d34461 1232 found=true;
1233 }
1234 if(found) {
1235 cerr<<"Attempting to add CNAME to "<<rr.qname<<" which already had existing records"<<endl;
1236 return EXIT_FAILURE;
1237 }
1238 }
1239 else {
c07a0c6d 1240 while(B.get(oldrr)) {
1241 if(oldrr.qtype.getCode() == QType::CNAME)
66d34461 1242 found=true;
1243 }
1244 if(found) {
1245 cerr<<"Attempting to add record to "<<rr.qname<<" which already had a CNAME record"<<endl;
1246 return EXIT_FAILURE;
1247 }
1248 }
1249
17df2abf 1250 if(!addOrReplace) {
1251 cout<<"Current records for "<<rr.qname.toString()<<" IN "<<rr.qtype.getName()<<" will be replaced"<<endl;
1252 }
1253 for(auto i = contentStart ; i < cmds.size() ; ++i) {
1254 rr.content = DNSRecordContent::mastermake(rr.qtype.getCode(), 1, cmds[i])->getZoneRepresentation(true);
1255
1256 newrrs.push_back(rr);
1257 }
66d34461 1258
17df2abf 1259
1260 di.backend->replaceRRSet(di.id, name, rr.qtype, newrrs);
1261 // need to be explicit to bypass the ueberbackend cache!
1262 di.backend->lookup(rr.qtype, name, 0, di.id);
1263 cout<<"New rrset:"<<endl;
1264 while(di.backend->get(rr)) {
1265 cout<<rr.qname.toString()<<" IN "<<rr.qtype.getName()<<" "<<rr.ttl<<" "<<rr.content<<endl;
1266 }
66d34461 1267 return 1;
1268}
1269
1270// delete-rrset zone name type
1271int deleteRRSet(const std::string& zone_, const std::string& name_, const std::string& type_)
1272{
1273 UeberBackend B;
1274 DomainInfo di;
1275 DNSName zone(zone_);
1276 if(!B.getDomainInfo(zone, di)) {
1277 cerr<<"Domain '"<<zone<<"' does not exist"<<endl;
1278 return 1;
1279 }
1280
1281 DNSName name;
1282 if(name_=="@")
1283 name=zone;
1284 else
1285 name=DNSName(name_)+zone;
1286
1287 QType qt(QType::chartocode(type_.c_str()));
1288 di.backend->replaceRRSet(di.id, name, qt, vector<DNSResourceRecord>());
1289 return 0;
1290}
c9865bc5 1291
22c68348 1292int listAllZones(const string &type="") {
bd108049
RA
1293
1294 int kindFilter = -1;
1295 if (type.size()) {
1296 if (toUpper(type) == "MASTER")
1297 kindFilter = 0;
1298 else if (toUpper(type) == "SLAVE")
1299 kindFilter = 1;
1300 else if (toUpper(type) == "NATIVE")
1301 kindFilter = 2;
22c68348 1302 else {
fd5076c8 1303 cerr<<"Syntax: pdnsutil list-all-zones [master|slave|native]"<<endl;
22c68348
KM
1304 return 1;
1305 }
bd108049
RA
1306 }
1307
8ecfea3a 1308 UeberBackend B("default");
bd108049 1309
8ecfea3a 1310 vector<DomainInfo> domains;
9c4a4ddc 1311 B.getAllDomains(&domains, true);
8ecfea3a
KM
1312
1313 int count = 0;
bd108049
RA
1314 for (vector<DomainInfo>::const_iterator di=domains.begin(); di != domains.end(); di++) {
1315 if (di->kind == kindFilter || kindFilter == -1) {
675fa24c 1316 cout<<di->zone.toString()<<endl;
bd108049
RA
1317 count++;
1318 }
1319 }
1320
1321 if (kindFilter != -1)
66d34461 1322 cout<<type<<" zonecount: "<<count<<endl;
bd108049 1323 else
66d34461 1324 cout<<"All zonecount: "<<count<<endl;
bd108049
RA
1325 return 0;
1326}
1327
166d8647 1328bool testAlgorithm(int algo)
cbb0025b 1329{
166d8647 1330 return DNSCryptoKeyEngine::testOne(algo);
cbb0025b 1331}
1325e8a2 1332
166d8647 1333bool testAlgorithms()
189bb9d2 1334{
166d8647 1335 return DNSCryptoKeyEngine::testAll();
189bb9d2
BH
1336}
1337
b873e23a 1338void testSpeed(DNSSECKeeper& dk, const DNSName& zone, const string& remote, int cores)
ea937fd4
BH
1339{
1340 DNSResourceRecord rr;
b873e23a 1341 rr.qname=DNSName("blah")+zone;
ea937fd4
BH
1342 rr.qtype=QType::A;
1343 rr.ttl=3600;
1344 rr.auth=1;
703761cc 1345 rr.qclass = QClass::IN;
ea937fd4 1346 rr.d_place=DNSResourceRecord::ANSWER;
ea937fd4 1347
49449751 1348 UeberBackend db("key-only");
ea937fd4 1349
f0c4b9d5
PD
1350 if ( ! db.backends.size() )
1351 {
1352 throw runtime_error("No backends available for DNSSEC key storage");
1353 }
1354
b873e23a 1355 ChunkedSigningPipe csp(DNSName(zone), 1, remote, cores);
ea937fd4
BH
1356
1357 vector<DNSResourceRecord> signatures;
1358 uint32_t rnd;
1359 unsigned char* octets = (unsigned char*)&rnd;
1360 char tmp[25];
1361 DTime dt;
1362 dt.set();
1363 for(unsigned int n=0; n < 100000; ++n) {
1364 rnd = random();
1365 snprintf(tmp, sizeof(tmp), "%d.%d.%d.%d",
1366 octets[0], octets[1], octets[2], octets[3]);
1367 rr.content=tmp;
1368
1369 snprintf(tmp, sizeof(tmp), "r-%u", rnd);
b873e23a 1370 rr.qname=DNSName(tmp)+zone;
ea937fd4
BH
1371
1372 if(csp.submit(rr))
1373 while(signatures = csp.getChunk(), !signatures.empty())
1374 ;
1375 }
1376 cerr<<"Flushing the pipe, "<<csp.d_signed<<" signed, "<<csp.d_queued<<" queued, "<<csp.d_outstanding<<" outstanding"<< endl;
451ba512 1377 cerr<<"Net speed: "<<csp.d_signed/ (dt.udiffNoReset()/1000000.0) << " sigs/s"<<endl;
ea937fd4
BH
1378 while(signatures = csp.getChunk(true), !signatures.empty())
1379 ;
1380 cerr<<"Done, "<<csp.d_signed<<" signed, "<<csp.d_queued<<" queued, "<<csp.d_outstanding<<" outstanding"<< endl;
451ba512 1381 cerr<<"Net speed: "<<csp.d_signed/ (dt.udiff()/1000000.0) << " sigs/s"<<endl;
ea937fd4
BH
1382}
1383
aa65a832
BH
1384void verifyCrypto(const string& zone)
1385{
1386 ZoneParserTNG zpt(zone);
1387 DNSResourceRecord rr;
1388 DNSKEYRecordContent drc;
1389 RRSIGRecordContent rrc;
c3e26094 1390 DSRecordContent dsrc;
aa65a832 1391 vector<shared_ptr<DNSRecordContent> > toSign;
675fa24c 1392 DNSName qname, apex;
c3e26094 1393 dsrc.d_digesttype=0;
aa65a832
BH
1394 while(zpt.get(rr)) {
1395 if(rr.qtype.getCode() == QType::DNSKEY) {
1396 cerr<<"got DNSKEY!"<<endl;
c3e26094 1397 apex=rr.qname;
aa65a832
BH
1398 drc = *dynamic_cast<DNSKEYRecordContent*>(DNSRecordContent::mastermake(QType::DNSKEY, 1, rr.content));
1399 }
1400 else if(rr.qtype.getCode() == QType::RRSIG) {
1401 cerr<<"got RRSIG"<<endl;
1402 rrc = *dynamic_cast<RRSIGRecordContent*>(DNSRecordContent::mastermake(QType::RRSIG, 1, rr.content));
1403 }
c3e26094
BH
1404 else if(rr.qtype.getCode() == QType::DS) {
1405 cerr<<"got DS"<<endl;
1406 dsrc = *dynamic_cast<DSRecordContent*>(DNSRecordContent::mastermake(QType::DS, 1, rr.content));
1407 }
aa65a832
BH
1408 else {
1409 qname = rr.qname;
aa65a832
BH
1410 toSign.push_back(shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(rr.qtype.getCode(), 1, rr.content)));
1411 }
1412 }
c3e26094 1413
f309dacd 1414 string msg = getMessageForRRSET(qname, rrc, toSign);
49449751 1415 cerr<<"Verify: "<<DNSCryptoKeyEngine::makeFromPublicKeyString(drc.d_algorithm, drc.d_key)->verify(msg, rrc.d_signature)<<endl;
c3e26094 1416 if(dsrc.d_digesttype) {
675fa24c
PD
1417 cerr<<"Calculated DS: "<<apex.toString()<<" IN DS "<<makeDSFromDNSKey(apex, drc, dsrc.d_digesttype).getZoneRepresentation()<<endl;
1418 cerr<<"Original DS: "<<apex.toString()<<" IN DS "<<dsrc.getZoneRepresentation()<<endl;
c3e26094 1419 }
295812a1 1420#if 0
8d9f38f2 1421 DNSCryptoKeyEngine*key=DNSCryptoKeyEngine::makeFromISCString(drc, "Private-key-format: v1.2\n"
295812a1
BH
1422 "Algorithm: 12 (ECC-GOST)\n"
1423 "GostAsn1: MEUCAQAwHAYGKoUDAgITMBIGByqFAwICIwEGByqFAwICHgEEIgQg/9MiXtXKg9FDXDN/R9CmVhJDyuzRAIgh4tPwCu4NHIs=\n");
1424 string resign=key->sign(hash);
1425 cerr<<Base64Encode(resign)<<endl;
8d9f38f2 1426 cerr<<"Verify: "<<DNSCryptoKeyEngine::makeFromPublicKeyString(drc.d_algorithm, drc.d_key)->verify(hash, resign)<<endl;
295812a1
BH
1427#endif
1428
aa65a832 1429}
675fa24c 1430bool disableDNSSECOnZone(DNSSECKeeper& dk, const DNSName& zone)
5935cede 1431{
032e3906
PD
1432 UeberBackend B("default");
1433 DomainInfo di;
1434
1435 if (!B.getDomainInfo(zone, di)){
1436 cerr << "No such zone in the database" << endl;
1437 return false;
1438 }
1439
5935cede 1440 if(!dk.isSecuredZone(zone)) {
451ba512 1441 cerr<<"Zone is not secured"<<endl;
032e3906 1442 return false;
5935cede
BH
1443 }
1444 DNSSECKeeper::keyset_t keyset=dk.getKeys(zone);
aa65a832 1445
5935cede 1446 if(keyset.empty()) {
675fa24c 1447 cerr << "No keys for zone '"<<zone.toString()<<"'."<<endl;
5935cede
BH
1448 }
1449 else {
ff05fd12 1450 for(DNSSECKeeper::keyset_t::value_type value : keyset) {
5935cede 1451 dk.deactivateKey(zone, value.second.id);
37fd8771 1452 dk.removeKey(zone, value.second.id);
5935cede
BH
1453 }
1454 }
1455 dk.unsetNSEC3PARAM(zone);
1456 dk.unsetPresigned(zone);
032e3906 1457 return true;
5935cede 1458}
bddc5d92 1459
1460int setZoneKind(const DNSName& zone, const DomainInfo::DomainKind kind)
1461{
1462 UeberBackend B("default");
1463 DomainInfo di;
1464 std::vector<std::string> meta;
1465
1466 if (!B.getDomainInfo(zone, di)){
1467 cerr << "No such zone "<<zone<<" in the database" << endl;
1468 return EXIT_FAILURE;
1469 }
1470 if(!di.backend->setKind(zone, kind)) {
1471 cerr<<"Could not find backend willing to accept new zone configuration"<<endl;
1472 return EXIT_FAILURE;
1473 }
1474 return EXIT_SUCCESS;
1475}
1476
675fa24c 1477bool showZone(DNSSECKeeper& dk, const DNSName& zone)
ade1b1e9 1478{
032e3906
PD
1479 UeberBackend B("default");
1480 DomainInfo di;
4831df57 1481 std::vector<std::string> meta;
032e3906
PD
1482
1483 if (!B.getDomainInfo(zone, di)){
1484 cerr << "No such zone in the database" << endl;
1485 return false;
1486 }
1487
176fdd7e 1488 cout<<"This is a "<<DomainInfo::getKindString(di.kind)<<" zone"<<endl;
1489 if(di.kind == DomainInfo::Master) {
1490 cout<<"Last SOA serial number we notified: "<<di.notified_serial<<" ";
1491 SOAData sd;
1492 if(B.getSOAUncached(zone, sd)) {
1493 if(sd.serial == di.notified_serial)
1494 cout<< "== ";
1495 else
1496 cout << "!= ";
1497 cout<<sd.serial<<" (serial in the database)"<<endl;
1498 }
1499 else
1500 cout<<"- no serial found in database"<<endl;
1501 }
1502 else if(di.kind == DomainInfo::Slave) {
1503 cout<<"Master"<<addS(di.masters)<<": ";
1504 for(const auto& m : di.masters)
1505 cout<<m<<" ";
1506 cout<<endl;
1507 struct tm tm;
1508 localtime_r(&di.last_check, &tm);
1509 char buf[80];
1510 if(di.last_check)
1511 strftime(buf, sizeof(buf)-1, "%a %F %H:%M:%S", &tm);
1512 else
1513 strncpy(buf, "Never", sizeof(buf)-1);
1514
1515 cout<<"Last time we got update from master: "<<buf<<endl;
1516 SOAData sd;
1517 if(B.getSOAUncached(zone, sd)) {
1518 cout<<"SOA serial in database: "<<sd.serial<<endl;
1519 cout<<"Refresh interval: "<<sd.refresh<<" seconds"<<endl;
1520 }
1521 else
1522 cout<<"No SOA serial found in database"<<endl;
1523 }
1524
1525
d3e7090c 1526 if(!dk.isSecuredZone(zone)) {
5911b051 1527 cout<<"Zone is not actively secured"<<endl;
d3e7090c 1528 }
ade1b1e9
BH
1529 NSEC3PARAMRecordContent ns3pr;
1530 bool narrow;
3c873e66 1531 bool haveNSEC3=dk.getNSEC3PARAM(zone, &ns3pr, &narrow);
ade1b1e9 1532
d4a4176d 1533 DNSSECKeeper::keyset_t keyset=dk.getKeys(zone);
4831df57 1534 if (B.getDomainMetadata(zone, "TSIG-ALLOW-AXFR", meta) && meta.size() > 0) {
5911b051 1535 cout << "Zone has following allowed TSIG key(s): " << boost::join(meta, ",") << endl;
4831df57
AT
1536 }
1537
a56bc64d 1538 meta.clear();
4831df57 1539 if (B.getDomainMetadata(zone, "AXFR-MASTER-TSIG", meta) && meta.size() > 0) {
5911b051 1540 cout << "Zone uses following TSIG key(s): " << boost::join(meta, ",") << endl;
4831df57 1541 }
d3e7090c 1542
5911b051 1543 std::map<std::string, std::vector<std::string> > metamap;
1544 if(B.getAllDomainMetadata(zone, metamap)) {
176fdd7e 1545 cout<<"Metadata items: ";
1546 if(metamap.empty())
1547 cout<<"None";
1548 cout<<endl;
1549
5911b051 1550 for(const auto& m : metamap) {
1551 for(const auto i : m.second)
1552 cout << '\t' << m.first<<'\t' << i <<endl;
1553 }
1554 }
1555
120b4dc6
AT
1556 if (dk.isPresigned(zone)) {
1557 cout <<"Zone is " << (dk.isPresigned(zone) ? "" : "not ") << "presigned"<<endl;
1558 // get us some keys
1559 vector<DNSKEYRecordContent> keys;
1560 DNSResourceRecord rr;
ade1b1e9 1561
120b4dc6
AT
1562 B.lookup(QType(QType::DNSKEY), DNSName(zone));
1563 while(B.get(rr)) {
1564 if (rr.qtype != QType::DNSKEY) continue;
1565 keys.push_back(*dynamic_cast<DNSKEYRecordContent*>(DNSKEYRecordContent::make(rr.getZoneRepresentation())));
1566 }
1567
1568 if(keys.empty()) {
1569 cerr << "No keys for zone '"<<zone.toString()<<"'."<<endl;
1570 return true;
1571 }
1572
1573 if(!haveNSEC3)
1574 cout<<"Zone has NSEC semantics"<<endl;
1575 else
1576 cout<<"Zone has " << (narrow ? "NARROW " : "") <<"hashed NSEC3 semantics, configuration: "<<ns3pr.getZoneRepresentation()<<endl;
1577 cout << "keys: "<<endl;
1578 sort(keys.begin(),keys.end());
1579 reverse(keys.begin(),keys.end());
3fc032b9 1580 bool shown=false;
120b4dc6
AT
1581 for(const auto& key : keys) {
1582 string algname;
1583 algorithm2name(key.d_algorithm,algname);
3fc032b9
AT
1584 int bits;
1585 if (key.d_key[0] == 0)
1586 bits = *(uint16_t*)(key.d_key.c_str()+1);
1587 else
1588 bits = *(uint8_t*)key.d_key.c_str();
1589 bits = (key.d_key.size() - (bits+1))*8;
1590 cout << (key.d_flags == 257 ? "KSK" : "ZSK") << ", tag = " << key.getTag() << ", algo = "<<(int)key.d_algorithm << ", bits = " << bits << endl;
1591 cout << "DNSKEY = " <<zone.toString()<<" IN DNSKEY "<< key.getZoneRepresentation() << "; ( " + algname + " ) " <<endl;
1592 if (shown) continue;
1593 shown=true;
120b4dc6
AT
1594 cout<<"DS = "<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, key, 1).getZoneRepresentation() << " ; ( SHA1 digest )" << endl;
1595 cout<<"DS = "<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, key, 2).getZoneRepresentation() << " ; ( SHA256 digest )" << endl;
1596 try {
1597 cout<<"DS = "<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, key, 3).getZoneRepresentation() << " ; ( GOST R 34.11-94 digest )" << endl;
1598 }
1599 catch(...)
1600 {}
1601 try {
1602 cout<<"DS = "<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, key, 4).getZoneRepresentation() << " ; ( SHA-384 digest )" << endl;
1603 }
1604 catch(...)
1605 {}
1606 }
1607 }
1608 else if(keyset.empty()) {
675fa24c 1609 cerr << "No keys for zone '"<<zone.toString()<<"'."<<endl;
ade1b1e9
BH
1610 }
1611 else {
d4a4176d
BH
1612 if(!haveNSEC3)
1613 cout<<"Zone has NSEC semantics"<<endl;
1614 else
1615 cout<<"Zone has " << (narrow ? "NARROW " : "") <<"hashed NSEC3 semantics, configuration: "<<ns3pr.getZoneRepresentation()<<endl;
1616
ade1b1e9 1617 cout << "keys: "<<endl;
ff05fd12 1618 for(DNSSECKeeper::keyset_t::value_type value : keyset) {
e0ad7bb1
PD
1619 string algname;
1620 algorithm2name(value.first.d_algorithm, algname);
95b3e84b
AT
1621 if (value.first.getKey()->getBits() < 1) {
1622 cout<<"ID = "<<value.second.id<<" ("<<(value.second.keyOrZone ? "KSK" : "ZSK")<<") <key missing or defunct>" <<endl;
1623 continue;
1624 }
ade1b1e9 1625 cout<<"ID = "<<value.second.id<<" ("<<(value.second.keyOrZone ? "KSK" : "ZSK")<<"), tag = "<<value.first.getDNSKEY().getTag();
3870e4d1 1626 cout<<", algo = "<<(int)value.first.d_algorithm<<", bits = "<<value.first.getKey()->getBits()<<"\t"<<((int)value.second.active == 1 ? " A" : "Ina")<<"ctive ( " + algname + " ) "<<endl;
c283741c 1627 if(value.second.keyOrZone || ::arg().mustDo("direct-dnskey") || 1)
675fa24c 1628 cout<<(value.second.keyOrZone ? "KSK" : "ZSK")<<" DNSKEY = "<<zone.toString()<<" IN DNSKEY "<< value.first.getDNSKEY().getZoneRepresentation() << " ; ( " + algname + " )" << endl;
c283741c 1629 if(value.second.keyOrZone || 1) {
675fa24c
PD
1630 cout<<"DS = "<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 1).getZoneRepresentation() << " ; ( SHA1 digest )" << endl;
1631 cout<<"DS = "<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 2).getZoneRepresentation() << " ; ( SHA256 digest )" << endl;
608d776f
BH
1632 try {
1633 string output=makeDSFromDNSKey(zone, value.first.getDNSKEY(), 3).getZoneRepresentation();
675fa24c 1634 cout<<"DS = "<<zone.toString()<<" IN DS "<< output << " ; ( GOST R 34.11-94 digest )" << endl;
e0ad7bb1
PD
1635 }
1636 catch(...)
1637 {
1638 }
1639 try {
1640 string output=makeDSFromDNSKey(zone, value.first.getDNSKEY(), 4).getZoneRepresentation();
675fa24c 1641 cout<<"DS = "<<zone.toString()<<" IN DS "<< output << " ; ( SHA-384 digest )" << endl;
608d776f
BH
1642 }
1643 catch(...)
1644 {
1645 }
1646 cout<<endl;
ade1b1e9
BH
1647 }
1648 }
1649 }
032e3906 1650 return true;
ade1b1e9 1651}
5d2e58b0 1652
675fa24c 1653bool secureZone(DNSSECKeeper& dk, const DNSName& zone)
ddac7145 1654{
36758d25
PD
1655 // parse attribute
1656 vector<string> k_algos;
1657 vector<string> z_algos;
1658 int k_size;
1659 int z_size;
1660
1661 stringtok(k_algos, ::arg()["default-ksk-algorithms"], " ,");
1662 k_size = ::arg().asNum("default-ksk-size");
1663 stringtok(z_algos, ::arg()["default-zsk-algorithms"], " ,");
1664 z_size = ::arg().asNum("default-zsk-size");
1665
1666 if (k_size < 0) {
1667 throw runtime_error("KSK key size must be equal to or greater than 0");
1668 }
1669
5209ee17
PD
1670 if (k_algos.size() < 1 && z_algos.size() < 1) {
1671 throw runtime_error("Zero algorithms given for KSK+ZSK in total");
36758d25
PD
1672 }
1673
1674 if (z_size < 0) {
1675 throw runtime_error("ZSK key size must be equal to or greater than 0");
1676 }
1677
f60f4bcd 1678 if(dk.isSecuredZone(zone)) {
fd5076c8 1679 cerr << "Zone '"<<zone.toString()<<"' already secure, remove keys with pdnsutil remove-zone-key if needed"<<endl;
f60f4bcd
BH
1680 return false;
1681 }
1682
2402c558
BH
1683 DomainInfo di;
1684 UeberBackend B("default");
1685 if(!B.getDomainInfo(zone, di) || !di.backend) { // di.backend and B are mostly identical
694ea7ae 1686 cerr<<"Can't find a zone called '"<<zone.toString()<<"'"<<endl;
2402c558
BH
1687 return false;
1688 }
1689
3bf07122
PD
1690 if(di.kind == DomainInfo::Slave)
1691 {
694ea7ae
CH
1692 cerr<<"Warning! This is a slave domain! If this was a mistake, please run"<<endl;
1693 cerr<<"pdnsutil disable-dnssec "<<zone.toString()<<" right now!"<<endl;
3bf07122
PD
1694 }
1695
36758d25 1696 if (k_size)
5209ee17 1697 cout << "Securing zone with key size " << k_size << endl;
36758d25 1698 else
5209ee17 1699 cout << "Securing zone with default key size" << endl;
f60f4bcd 1700
f60f4bcd
BH
1701
1702 DNSSECKeeper::keyset_t zskset=dk.getKeys(zone, false);
1703
1704 if(!zskset.empty()) {
675fa24c 1705 cerr<<"There were ZSKs already for zone '"<<zone.toString()<<"', no need to add more"<<endl;
f60f4bcd
BH
1706 return false;
1707 }
36758d25 1708
5209ee17
PD
1709 for(auto &k_algo: k_algos) {
1710 cout << "Adding KSK with algorithm " << k_algo << endl;
1711
1712 int algo = shorthand2algorithm(k_algo);
1713
1714 if(!dk.addKey(zone, true, algo, k_size, true)) {
1715 cerr<<"No backend was able to secure '"<<zone.toString()<<"', most likely because no DNSSEC"<<endl;
1716 cerr<<"capable backends are loaded, or because the backends have DNSSEC disabled."<<endl;
1717 cerr<<"For the Generic SQL backends, set the 'gsqlite3-dnssec', 'gmysql-dnssec' or"<<endl;
1718 cerr<<"'gpgsql-dnssec' flag. Also make sure the schema has been updated for DNSSEC!"<<endl;
1719 return false;
1720 }
1721 }
1722
1723 for(auto &z_algo : z_algos)
36758d25 1724 {
5209ee17
PD
1725 cout << "Adding ZSK with algorithm " << z_algo << endl;
1726
36758d25 1727 int algo = shorthand2algorithm(z_algo);
5209ee17
PD
1728
1729 if(!dk.addKey(zone, false, algo, z_size, true)) {
36a885a9
PD
1730 cerr<<"No backend was able to secure '"<<zone.toString()<<"', most likely because no DNSSEC"<<endl;
1731 cerr<<"capable backends are loaded, or because the backends have DNSSEC disabled."<<endl;
1732 cerr<<"For the Generic SQL backends, set the 'gsqlite3-dnssec', 'gmysql-dnssec' or"<<endl;
1733 cerr<<"'gpgsql-dnssec' flag. Also make sure the schema has been updated for DNSSEC!"<<endl;
1734 return false;
1735 }
1736 }
1737
f60f4bcd 1738 if(!dk.isSecuredZone(zone)) {
451ba512
AT
1739 cerr<<"Failed to secure zone. Is your backend dnssec enabled? (set "<<endl;
1740 cerr<<"gsqlite3-dnssec, or gmysql-dnssec etc). Check this first."<<endl;
1741 cerr<<"If you run with the BIND backend, make sure you have configured"<<endl;
1742 cerr<<"it to use DNSSEC with 'bind-dnssec-db=/path/fname' and"<<endl;
fd5076c8 1743 cerr<<"'pdnsutil create-bind-db /path/fname'!"<<endl;
f60f4bcd 1744 return false;
36758d25
PD
1745 }
1746
f60f4bcd
BH
1747 // rectifyZone(dk, zone);
1748 // showZone(dk, zone);
675fa24c 1749 cout<<"Zone "<<zone.toString()<<" secured"<<endl;
d6c16697 1750 return true;
ddac7145
BH
1751}
1752
675fa24c 1753void testSchema(DNSSECKeeper& dk, const DNSName& zone)
46d2e0d6
PD
1754{
1755 cout<<"Note: test-schema will try to create the zone, but it will not remove it."<<endl;
1756 cout<<"Please clean up after this."<<endl;
1757 cout<<endl;
1758 cout<<"Constructing UeberBackend"<<endl;
2402c558 1759 UeberBackend B("default");
46d2e0d6 1760 cout<<"Picking first backend - if this is not what you want, edit launch line!"<<endl;
2402c558 1761 DNSBackend *db = B.backends[0];
675fa24c 1762 cout<<"Creating slave domain "<<zone.toString()<<endl;
719f9024 1763 db->createSlaveDomain("127.0.0.1", zone, "", "_testschema");
46d2e0d6
PD
1764 cout<<"Slave domain created"<<endl;
1765
1766 DomainInfo di;
2402c558 1767 if(!B.getDomainInfo(zone, di) || !di.backend) { // di.backend and B are mostly identical
46d2e0d6
PD
1768 cout<<"Can't find domain we just created, aborting"<<endl;
1769 return;
1770 }
1771 db=di.backend;
1772 DNSResourceRecord rr, rrget;
1773 cout<<"Starting transaction to feed records"<<endl;
1774 db->startTransaction(zone, di.id);
1775
1776 rr.qtype=QType::SOA;
1777 rr.qname=zone;
1778 rr.ttl=86400;
1779 rr.domain_id=di.id;
1780 rr.auth=1;
1781 rr.content="ns1.example.com. ahu.example.com. 2012081039 7200 3600 1209600 3600";
1782 cout<<"Feeding SOA"<<endl;
1783 db->feedRecord(rr);
1784 rr.qtype=QType::TXT;
1785 // 300 As
1786 rr.content="\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"";
1787 cout<<"Feeding overlong TXT"<<endl;
1788 db->feedRecord(rr);
1789 cout<<"Committing"<<endl;
1790 db->commitTransaction();
1791 cout<<"Querying TXT"<<endl;
1792 db->lookup(QType(QType::TXT), zone, NULL, di.id);
1793 if(db->get(rrget))
1794 {
1795 DNSResourceRecord rrthrowaway;
1796 if(db->get(rrthrowaway)) // should not touch rr but don't assume anything
1797 {
1798 cout<<"Expected one record, got multiple, aborting"<<endl;
1799 return;
1800 }
1801 int size=rrget.content.size();
1802 if(size != 302)
1803 {
1804 cout<<"Expected 302 bytes, got "<<size<<", aborting"<<endl;
1805 return;
1806 }
1807 }
1808 cout<<"[+] content field is over 255 bytes"<<endl;
1809
1810 cout<<"Dropping all records, inserting SOA+2xA"<<endl;
1811 db->startTransaction(zone, di.id);
1812
1813 rr.qtype=QType::SOA;
1814 rr.qname=zone;
1815 rr.ttl=86400;
1816 rr.domain_id=di.id;
1817 rr.auth=1;
1818 rr.content="ns1.example.com. ahu.example.com. 2012081039 7200 3600 1209600 3600";
1819 cout<<"Feeding SOA"<<endl;
1820 db->feedRecord(rr);
1821
1822 rr.qtype=QType::A;
b873e23a 1823 rr.qname=DNSName("_underscore")+zone;
46d2e0d6
PD
1824 rr.content="127.0.0.1";
1825 db->feedRecord(rr);
1826
b873e23a 1827 rr.qname=DNSName("bla")+zone;
46d2e0d6
PD
1828 cout<<"Committing"<<endl;
1829 db->commitTransaction();
1830
1831 cout<<"Securing zone"<<endl;
1832 secureZone(dk, zone);
1833 cout<<"Rectifying zone"<<endl;
1834 rectifyZone(dk, zone);
1835 cout<<"Checking underscore ordering"<<endl;
675fa24c 1836 DNSName before, after;
b873e23a 1837 db->getBeforeAndAfterNames(di.id, zone, DNSName("z")+zone, before, after);
675fa24c 1838 cout<<"got '"<<before.toString()<<"' < 'z."<<zone.toString()<<"' < '"<<after.toString()<<"'"<<endl;
b873e23a 1839 if(before != DNSName("_underscore")+zone)
46d2e0d6 1840 {
675fa24c 1841 cout<<"before is wrong, got '"<<before.toString()<<"', expected '_underscore."<<zone.toString()<<"', aborting"<<endl;
46d2e0d6
PD
1842 return;
1843 }
1844 if(after != zone)
1845 {
675fa24c 1846 cout<<"after is wrong, got '"<<after.toString()<<"', expected '"<<zone.toString()<<"', aborting"<<endl;
46d2e0d6
PD
1847 return;
1848 }
1849 cout<<"[+] ordername sorting is correct for names starting with _"<<endl;
1850 cout<<endl;
675fa24c 1851 cout<<"End of tests, please remove "<<zone.toString()<<" from domains+records"<<endl;
46d2e0d6
PD
1852}
1853
1d211b1b 1854int main(int argc, char** argv)
20002664 1855try
2fa33bce 1856{
1d211b1b
BH
1857 po::options_description desc("Allowed options");
1858 desc.add_options()
1859 ("help,h", "produce help message")
b3ce3dec 1860 ("verbose,v", "be verbose")
1d211b1b 1861 ("force", "force an action")
aa952078 1862 ("config-name", po::value<string>()->default_value(""), "virtual configuration name")
7d9dcde0 1863 ("config-dir", po::value<string>()->default_value(SYSCONFDIR), "location of pdns.conf")
1d211b1b
BH
1864 ("commands", po::value<vector<string> >());
1865
1866 po::positional_options_description p;
1867 p.add("commands", -1);
1868 po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), g_vm);
1869 po::notify(g_vm);
1870
1871 vector<string> cmds;
1872
1873 if(g_vm.count("commands"))
1874 cmds = g_vm["commands"].as<vector<string> >();
1875
b3ce3dec
BH
1876 g_verbose = g_vm.count("verbose");
1877
1d211b1b 1878 if(cmds.empty() || g_vm.count("help")) {
fd5076c8 1879 cerr<<"Usage: \npdnsutil [options] <command> [params ..]\n"<<endl;
451ba512 1880 cerr<<"Commands:"<<endl;
ec53ae22 1881 cerr<<"activate-tsig-key ZONE NAME {master|slave}"<<endl;
da7f2896 1882 cerr<<" Enable TSIG key for a zone"<<endl;
451ba512 1883 cerr<<"activate-zone-key ZONE KEY-ID Activate the key with key id KEY-ID in ZONE"<<endl;
66d34461 1884 cerr<<"add-record ZONE NAME TYPE [ttl] content"<<endl;
1885 cerr<<" [content..] Add one or more records to ZONE"<<endl;
d9db3d7e 1886 cerr<<"add-zone-key ZONE {zsk|ksk} [BITS] [active|inactive]"<<endl;
feafa2f3
KM
1887 cerr<<" [rsasha1|rsasha256|rsasha512|gost|ecdsa256|ecdsa384";
1888#ifdef HAVE_LIBSODIUM
1889 cerr<<"|experimental-ed25519";
1890#endif
1891 cerr<<"]"<<endl;
451ba512 1892 cerr<<" Add a ZSK or KSK to zone and specify algo&bits"<<endl;
f641c3b7 1893 cerr<<"backend-cmd BACKEND CMD [CMD..] Perform one or more backend commands"<<endl;
ec53ae22 1894 cerr<<"b2b-migrate OLD NEW Move all data from one backend to another"<<endl;
a0a6cb58 1895 cerr<<"bench-db [filename] Bench database backend with queries, one domain per line"<<endl;
451ba512 1896 cerr<<"check-zone ZONE Check a zone for correctness"<<endl;
d6ddb28a
PL
1897 cerr<<"check-all-zones [exit-on-error] Check all zones for correctness. Set exit-on-error to exit immediately"<<endl;
1898 cerr<<" after finding an error in a zone."<<endl;
ca8e1742 1899 cerr<<"create-bind-db FNAME Create DNSSEC db for BIND backend (bind-dnssec-db)"<<endl;
66d34461 1900 cerr<<"create-zone ZONE [nsname] Create empty zone ZONE"<<endl;
ec53ae22 1901 cerr<<"deactivate-tsig-key ZONE NAME {master|slave}"<<endl;
da7f2896 1902 cerr<<" Disable TSIG key for a zone"<<endl;
451ba512 1903 cerr<<"deactivate-zone-key ZONE KEY-ID Deactivate the key with key id KEY-ID in ZONE"<<endl;
66d34461 1904 cerr<<"delete-rrset ZONE NAME TYPE Delete named RRSET from zone"<<endl;
451ba512 1905 cerr<<"delete-tsig-key NAME Delete TSIG key (warning! will not unmap key!)"<<endl;
17d89a3e 1906 cerr<<"delete-zone ZONE Delete the zone"<<endl;
451ba512 1907 cerr<<"disable-dnssec ZONE Deactivate all keys and unset PRESIGNED in ZONE"<<endl;
c2e0881c 1908 cerr<<"edit-zone ZONE Edit zone contents using $EDITOR"<<endl;
451ba512
AT
1909 cerr<<"export-zone-dnskey ZONE KEY-ID Export to stdout the public DNSKEY described"<<endl;
1910 cerr<<"export-zone-key ZONE KEY-ID Export to stdout the private key described"<<endl;
da7f2896 1911 cerr<<"generate-tsig-key NAME ALGORITHM Generate new TSIG key"<<endl;
ec53ae22
PL
1912 cerr<<"generate-zone-key {zsk|ksk} [ALGORITHM] [BITS]"<<endl;
1913 cerr<<" Generate a ZSK or KSK to stdout with specified ALGORITHM and BITS"<<endl;
1914 cerr<<"get-meta ZONE [KIND ...] Get zone metadata. If no KIND given, lists all known"<<endl;
451ba512 1915 cerr<<"hash-zone-record ZONE RNAME Calculate the NSEC3 hash for RNAME in ZONE"<<endl;
3c12a7e9 1916#ifdef HAVE_P11KIT1
ec53ae22 1917 cerr<<"hsm assign ZONE ALGORITHM {ksk|zsk} MODULE SLOT PIN LABEL"<<endl<<
3c12a7e9 1918 " Assign a hardware signing module to a ZONE"<<endl;
ec53ae22
PL
1919 cerr<<"hsm create-key ZONE KEY-ID [BITS] Create a key using hardware signing module for ZONE (use assign first)"<<endl;
1920 cerr<<" BITS defaults to 2048"<<endl;
3c12a7e9 1921#endif
451ba512
AT
1922 cerr<<"increase-serial ZONE Increases the SOA-serial by 1. Uses SOA-EDIT"<<endl;
1923 cerr<<"import-tsig-key NAME ALGORITHM KEY Import TSIG key"<<endl;
ca8e1742 1924 cerr<<"import-zone-key ZONE FILE Import from a file a private key, ZSK or KSK"<<endl;
d9db3d7e 1925 cerr<<" [active|inactive] [ksk|zsk] Defaults to KSK and active"<<endl;
c9865bc5 1926 cerr<<"load-zone ZONE FILE Load ZONE from FILE, possibly creating zone or atomically"<<endl;
1927 cerr<<" replacing contents"<<endl;
597b9b3e 1928 cerr<<"list-keys [ZONE] List DNSSEC keys for ZONE. When ZONE is unset or \"all\", display all keys for all zones"<<endl;
c9865bc5 1929 cerr<<"list-zone ZONE List zone contents"<<endl;
cd76677f 1930 cerr<<"list-all-zones [master|slave|native]"<<endl;
c9865bc5 1931 cerr<<" List all zone names"<<endl;;
451ba512
AT
1932 cerr<<"list-tsig-keys List all TSIG keys"<<endl;
1933 cerr<<"rectify-zone ZONE [ZONE ..] Fix up DNSSEC fields (order, auth)"<<endl;
1934 cerr<<"rectify-all-zones Rectify all zones."<<endl;
1935 cerr<<"remove-zone-key ZONE KEY-ID Remove key with KEY-ID from ZONE"<<endl;
66d34461 1936 cerr<<"replace-rrset ZONE NAME TYPE [ttl] Replace named RRSET from zone"<<endl;
1937 cerr<<" content [content..]"<<endl;
f84dba01 1938 cerr<<"secure-all-zones [increase-serial] Secure all zones without keys."<<endl;
ec53ae22 1939 cerr<<"secure-zone ZONE [ZONE ..] Add KSK and two ZSKs for ZONE"<<endl;
bddc5d92 1940 cerr<<"set-kind ZONE KIND Change the kind of ZONE to KIND (master, slave native)"<<endl;
ec53ae22 1941 cerr<<"set-nsec3 ZONE ['PARAMS' [narrow]] Enable NSEC3 with PARAMS. Optionally narrow"<<endl;
451ba512 1942 cerr<<"set-presigned ZONE Use presigned RRSIGs from storage"<<endl;
088370cd 1943 cerr<<"set-publish-cdnskey ZONE Enable sending CDNSKEY responses for ZONE"<<endl;
ef542223 1944 cerr<<"set-publish-cds ZONE [DIGESTALGOS] Enable sending CDS responses for ZONE, using DIGESTALGOS as signature algirithms"<<endl;
ec53ae22 1945 cerr<<" DIGESTALGOS should be a comma separated list of numbers, is is '1,2' by default"<<endl;
5911b051 1946 cerr<<"set-meta ZONE KIND [VALUE] [VALUE] Set zone metadata, optionally providing a value. Empty clears meta"<<endl;
1947 cerr<<" Note - this will replace all metadata records of KIND!"<<endl;
451ba512
AT
1948 cerr<<"show-zone ZONE Show DNSSEC (public) key details about a zone"<<endl;
1949 cerr<<"unset-nsec3 ZONE Switch back to NSEC"<<endl;
1950 cerr<<"unset-presigned ZONE No longer use presigned RRSIGs"<<endl;
088370cd 1951 cerr<<"unset-publish-cdnskey ZONE Disable sending CDNSKEY responses for ZONE"<<endl;
ef542223 1952 cerr<<"unset-publish-cds ZONE Disable sending CDS responses for ZONE"<<endl;
451ba512 1953 cerr<<"test-schema ZONE Test DB schema - will create ZONE"<<endl;
1d211b1b
BH
1954 cerr<<desc<<endl;
1955 return 0;
1956 }
ca8e1742 1957
7581a35b
KM
1958loadMainConfig(g_vm["config-dir"].as<string>());
1959
1960seedRandom(::arg()["entropy-source"]);
1961
d4f29089
KM
1962#ifdef HAVE_LIBSODIUM
1963 if (sodium_init() == -1) {
1964 cerr<<"Unable to initialize sodium crypto library"<<endl;
1965 exit(99);
1966 }
1967#endif
1968
7581a35b
KM
1969#ifdef HAVE_OPENSSL
1970 openssl_seed();
1971#endif
1972
cbb0025b 1973 if (cmds[0] == "test-algorithm") {
2551cc18 1974 if(cmds.size() != 2) {
fd5076c8 1975 cerr << "Syntax: pdnsutil test-algorithm algonum"<<endl;
2551cc18
PD
1976 return 0;
1977 }
335da0ba 1978 if (testAlgorithm(pdns_stou(cmds[1])))
166d8647
KM
1979 return 0;
1980 return 1;
cbb0025b
PD
1981 }
1982
ea937fd4 1983 if(cmds[0] == "test-algorithms") {
166d8647
KM
1984 if (testAlgorithms())
1985 return 0;
1986 return 1;
ea937fd4
BH
1987 }
1988
6dfa0aa0 1989 reportAllTypes();
b925384e 1990
2717b8b3 1991 if(cmds[0] == "create-bind-db") {
a2b0e9b5 1992#ifdef HAVE_SQLITE3
fbe72b7a 1993 if(cmds.size() != 2) {
fd5076c8 1994 cerr << "Syntax: pdnsutil create-bind-db FNAME"<<endl;
fbe72b7a
BH
1995 return 0;
1996 }
080f5d57 1997 try {
a2b0e9b5
KM
1998 SSQLite3 db(cmds[1], true); // create=ok
1999 vector<string> statements;
2000 stringtok(statements, sqlCreate, ";");
ff05fd12 2001 for(const string& statement : statements) {
0f310932
AT
2002 db.execute(statement);
2003 }
080f5d57 2004 }
a2b0e9b5
KM
2005 catch(SSqlException& se) {
2006 throw PDNSException("Error creating database in BIND backend: "+se.txtReason());
080f5d57
PD
2007 }
2008 return 0;
a2b0e9b5 2009#else
bd6c6334 2010 cerr<<"bind-dnssec-db requires building PowerDNS with SQLite3"<<endl;
a2b0e9b5
KM
2011 return 1;
2012#endif
2717b8b3 2013 }
9a798fb0 2014
fbe72b7a
BH
2015 DNSSECKeeper dk;
2016
46d2e0d6
PD
2017 if (cmds[0] == "test-schema") {
2018 if(cmds.size() != 2) {
fd5076c8 2019 cerr << "Syntax: pdnsutil test-schema ZONE"<<endl;
46d2e0d6
PD
2020 return 0;
2021 }
b873e23a 2022 testSchema(dk, DNSName(cmds[1]));
46d2e0d6
PD
2023 return 0;
2024 }
fbe72b7a 2025 if(cmds[0] == "rectify-zone") {
d4904322 2026 if(cmds.size() < 2) {
fd5076c8 2027 cerr << "Syntax: pdnsutil rectify-zone ZONE [ZONE..]"<<endl;
81b39e4b
BH
2028 return 0;
2029 }
032e3906 2030 unsigned int exitCode = 0;
d4904322 2031 for(unsigned int n = 1; n < cmds.size(); ++n)
b873e23a 2032 if (!rectifyZone(dk, DNSName(cmds[n])))
2033 exitCode = 1;
032e3906 2034 return exitCode;
20002664 2035 }
1325e8a2
PD
2036 else if (cmds[0] == "rectify-all-zones") {
2037 rectifyAllZones(dk);
2038 }
9abd98d3 2039 else if(cmds[0] == "check-zone") {
5d2e58b0 2040 if(cmds.size() != 2) {
fd5076c8 2041 cerr << "Syntax: pdnsutil check-zone ZONE"<<endl;
5d2e58b0
BH
2042 return 0;
2043 }
ba86110a 2044 UeberBackend B("default");
b873e23a 2045 exit(checkZone(dk, B, DNSName(cmds[1])));
5d2e58b0 2046 }
a0a6cb58 2047 else if(cmds[0] == "bench-db") {
2048 dbBench(cmds.size() > 1 ? cmds[1] : "");
2049 }
1325e8a2 2050 else if (cmds[0] == "check-all-zones") {
f6eb8c48 2051 bool exitOnError = ((cmds.size() >= 2 ? cmds[1] : "") == "exit-on-error");
d6ddb28a 2052 exit(checkAllZones(dk, exitOnError));
1325e8a2 2053 }
bd108049 2054 else if (cmds[0] == "list-all-zones") {
22c68348 2055 if (cmds.size() > 2) {
fd5076c8 2056 cerr << "Syntax: pdnsutil list-all-zones [master|slave|native]"<<endl;
22c68348
KM
2057 return 0;
2058 }
2059 if (cmds.size() == 2)
2060 return listAllZones(cmds[1]);
2061 return listAllZones();
bd108049 2062 }
81e9ba89
PD
2063 else if (cmds[0] == "test-zone") {
2064 cerr << "Did you mean check-zone?"<<endl;
2065 return 0;
2066 }
2067 else if (cmds[0] == "test-all-zones") {
2068 cerr << "Did you mean check-all-zones?"<<endl;
2069 return 0;
2070 }
49449751
BH
2071#if 0
2072 else if(cmds[0] == "signing-server" )
2073 {
2074 signingServer();
2075 }
2076 else if(cmds[0] == "signing-slave")
2077 {
2078 launchSigningService(0);
2079 }
2080#endif
ea937fd4 2081 else if(cmds[0] == "test-speed") {
37fd8771 2082 if(cmds.size() < 2) {
fd5076c8 2083 cerr << "Syntax: pdnsutil test-speed numcores [signing-server]"<<endl;
ea937fd4
BH
2084 return 0;
2085 }
335da0ba 2086 testSpeed(dk, DNSName(cmds[1]), (cmds.size() > 3) ? cmds[3] : "", pdns_stou(cmds[2]));
189bb9d2 2087 }
aa65a832
BH
2088 else if(cmds[0] == "verify-crypto") {
2089 if(cmds.size() != 2) {
fd5076c8 2090 cerr << "Syntax: pdnsutil verify-crypto FILE"<<endl;
aa65a832
BH
2091 return 0;
2092 }
2093 verifyCrypto(cmds[1]);
2094 }
da11ed0e 2095 else if(cmds[0] == "show-zone") {
a0472099 2096 if(cmds.size() != 2) {
fd5076c8 2097 cerr << "Syntax: pdnsutil show-zone ZONE"<<endl;
a0472099
BH
2098 return 0;
2099 }
b873e23a 2100 if (!showZone(dk, DNSName(cmds[1]))) return 1;
1d211b1b 2101 }
5935cede
BH
2102 else if(cmds[0] == "disable-dnssec") {
2103 if(cmds.size() != 2) {
fd5076c8 2104 cerr << "Syntax: pdnsutil disable-dnssec ZONE"<<endl;
5935cede
BH
2105 return 0;
2106 }
b873e23a 2107 DNSName zone(cmds[1]);
0b8e59ea
AT
2108 if(!disableDNSSECOnZone(dk, zone)) {
2109 cerr << "Cannot disable DNSSEC on " << zone << endl;
032e3906 2110 return 1;
0b8e59ea 2111 }
5935cede 2112 }
bed962b5 2113 else if(cmds[0] == "activate-zone-key") {
37fd8771 2114 if(cmds.size() != 3) {
fd5076c8 2115 cerr << "Syntax: pdnsutil activate-zone-key ZONE KEY-ID"<<endl;
37fd8771
BH
2116 return 0;
2117 }
b873e23a 2118 DNSName zone(cmds[1]);
335da0ba 2119 unsigned int id=pdns_stou(cmds[2]);
77b909cc
PD
2120 if(!id)
2121 {
2122 cerr<<"Invalid KEY-ID"<<endl;
2123 return 1;
2124 }
a84a8203
PD
2125 if (!dk.activateKey(zone, id)) {
2126 cerr<<"Activation of key failed"<<endl;
2127 return 1;
2128 }
2129 return 0;
bed962b5
BH
2130 }
2131 else if(cmds[0] == "deactivate-zone-key") {
37fd8771 2132 if(cmds.size() != 3) {
fd5076c8 2133 cerr << "Syntax: pdnsutil deactivate-zone-key ZONE KEY-ID"<<endl;
37fd8771
BH
2134 return 0;
2135 }
b873e23a 2136 DNSName zone(cmds[1]);
335da0ba 2137 unsigned int id=pdns_stou(cmds[2]);
77b909cc
PD
2138 if(!id)
2139 {
2140 cerr<<"Invalid KEY-ID"<<endl;
2141 return 1;
2142 }
a84a8203
PD
2143 if (!dk.deactivateKey(zone, id)) {
2144 cerr<<"Deactivation of key failed"<<endl;
2145 return 1;
2146 }
2147 return 0;
bed962b5
BH
2148 }
2149 else if(cmds[0] == "add-zone-key") {
37fd8771 2150 if(cmds.size() < 3 ) {
fd5076c8 2151 cerr << "Syntax: pdnsutil add-zone-key ZONE zsk|ksk [bits] [rsasha1|rsasha256|rsasha512|gost|ecdsa256|ecdsa384]"<<endl;
37fd8771
BH
2152 return 0;
2153 }
b873e23a 2154 DNSName zone(cmds[1]);
032e3906
PD
2155
2156 UeberBackend B("default");
2157 DomainInfo di;
2158
2159 if (!B.getDomainInfo(zone, di)){
2160 cerr << "No such zone in the database" << endl;
2161 return 0;
2162 }
2163
36c394e5
BH
2164 // need to get algorithm, bits & ksk or zsk from commandline
2165 bool keyOrZone=false;
36758d25 2166 int tmp_algo=0;
36c394e5 2167 int bits=0;
0ba8e0f1 2168 int algorithm=8;
4af49b80 2169 bool active=false;
36c394e5
BH
2170 for(unsigned int n=2; n < cmds.size(); ++n) {
2171 if(pdns_iequals(cmds[n], "zsk"))
2172 keyOrZone = false;
2173 else if(pdns_iequals(cmds[n], "ksk"))
2174 keyOrZone = true;
36758d25
PD
2175 else if((tmp_algo = shorthand2algorithm(cmds[n]))>0) {
2176 algorithm = tmp_algo;
4af49b80 2177 } else if(pdns_iequals(cmds[n], "active")) {
2178 active=true;
d9db3d7e 2179 } else if(pdns_iequals(cmds[n], "inactive") || pdns_iequals(cmds[n], "passive")) { // 'passive' eventually needs to be removed
4af49b80 2180 active=false;
335da0ba
AT
2181 } else if(pdns_stou(cmds[n])) {
2182 bits = pdns_stou(cmds[n]);
4af49b80 2183 } else {
a254438f 2184 cerr<<"Unknown algorithm, key flag or size '"<<cmds[n]<<"'"<<endl;
07bf35d1 2185 exit(EXIT_FAILURE);;
36c394e5
BH
2186 }
2187 }
07bf35d1 2188 if(!dk.addKey(zone, keyOrZone, algorithm, bits, active)) {
2189 cerr<<"Adding key failed, perhaps DNSSEC not enabled in configuration?"<<endl;
2190 exit(1);
2191 }
2192 else {
2193 cerr<<"Added a " << (keyOrZone ? "KSK" : "ZSK")<<" with algorithm = "<<algorithm<<", active="<<active<<endl;
2194 if(bits)
232f0877 2195 cerr<<"Requested specific key size of "<<bits<<" bits"<<endl;
07bf35d1 2196 }
bed962b5
BH
2197 }
2198 else if(cmds[0] == "remove-zone-key") {
471304cc 2199 if(cmds.size() < 3) {
fd5076c8 2200 cerr<<"Syntax: pdnsutil remove-zone-key ZONE KEY-ID"<<endl;
471304cc
BH
2201 return 0;
2202 }
b873e23a 2203 DNSName zone(cmds[1]);
335da0ba 2204 unsigned int id=pdns_stou(cmds[2]);
a84a8203 2205 if (!dk.removeKey(zone, id)) {
0b8e59ea 2206 cerr<<"Cannot remove key " << id << " from " << zone <<endl;
a84a8203
PD
2207 return 1;
2208 }
2209 return 0;
bed962b5 2210 }
51f6bca1
RA
2211 else if(cmds[0] == "delete-zone") {
2212 if(cmds.size() != 2) {
fd5076c8 2213 cerr<<"Syntax: pdnsutil delete-zone ZONE"<<endl;
51f6bca1
RA
2214 return 0;
2215 }
b873e23a 2216 exit(deleteZone(DNSName(cmds[1])));
51f6bca1 2217 }
c9865bc5 2218 else if(cmds[0] == "create-zone") {
66d34461 2219 if(cmds.size() != 2 && cmds.size()!=3 ) {
2220 cerr<<"Syntax: pdnsutil create-zone ZONE [nsname]"<<endl;
2221 return 0;
2222 }
2223 exit(createZone(DNSName(cmds[1]), cmds.size() > 2 ? DNSName(cmds[2]): DNSName()));
2224 }
e85a76ad 2225 else if(cmds[0] == "create-slave-zone") {
2226 if(cmds.size() < 3 ) {
2227 cerr<<"Syntax: pdnsutil create-slave-zone ZONE master-ip [master-ip..]"<<endl;
2228 return 0;
2229 }
2230 exit(createSlaveZone(cmds));
2231 }
66d34461 2232 else if(cmds[0] == "add-record") {
2233 if(cmds.size() < 5) {
2234 cerr<<"Syntax: pdnsutil add-record ZONE name type [ttl] \"content\" [\"content\"...]"<<endl;
2235 return 0;
2236 }
2237 exit(addOrReplaceRecord(true, cmds));
2238 }
2239 else if(cmds[0] == "replace-rrset") {
2240 if(cmds.size() < 5) {
2241 cerr<<"Syntax: pdnsutil replace-record ZONE name type [ttl] \"content\" [\"content\"...]"<<endl;
2242 return 0;
2243 }
2244 exit(addOrReplaceRecord(false , cmds));
2245 }
2246 else if(cmds[0] == "delete-rrset") {
2247 if(cmds.size() != 4) {
2248 cerr<<"Syntax: pdnsutil delete-rrset ZONE name type"<<endl;
c9865bc5 2249 return 0;
2250 }
66d34461 2251 exit(deleteRRSet(cmds[1], cmds[2], cmds[3]));
c9865bc5 2252 }
2253 else if(cmds[0] == "list-zone") {
2254 if(cmds.size() != 2) {
fd5076c8 2255 cerr<<"Syntax: pdnsutil list-zone ZONE"<<endl;
c9865bc5 2256 return 0;
2257 }
2258 if(cmds[1]==".")
2259 cmds[1].clear();
2260
b873e23a 2261 exit(listZone(DNSName(cmds[1])));
c9865bc5 2262 }
c2e0881c 2263 else if(cmds[0] == "edit-zone") {
2264 if(cmds.size() != 2) {
2265 cerr<<"Syntax: pdnsutil edit-zone ZONE"<<endl;
2266 return 0;
2267 }
2268 if(cmds[1]==".")
2269 cmds[1].clear();
2270
2271 exit(editZone(dk, DNSName(cmds[1])));
2272 }
2273
597b9b3e
PL
2274 else if(cmds[0] == "list-keys") {
2275 if(cmds.size() > 2) {
fd5076c8 2276 cerr<<"Syntax: pdnsutil list-keys [ZONE]"<<endl;
597b9b3e
PL
2277 return 0;
2278 }
2279 string zname = (cmds.size() == 2) ? cmds[1] : "all";
2280 exit(listKeys(zname, dk));
2281 }
c9865bc5 2282 else if(cmds[0] == "load-zone") {
2283 if(cmds.size() != 3) {
fd5076c8 2284 cerr<<"Syntax: pdnsutil load-zone ZONE FILENAME"<<endl;
c9865bc5 2285 return 0;
2286 }
2287 if(cmds[1]==".")
2288 cmds[1].clear();
2289
b873e23a 2290 exit(loadZone(DNSName(cmds[1]), cmds[2]));
c9865bc5 2291 }
c3c89361 2292 else if(cmds[0] == "secure-zone") {
ddac7145 2293 if(cmds.size() < 2) {
fd5076c8 2294 cerr << "Syntax: pdnsutil secure-zone ZONE"<<endl;
a9175ad6
BH
2295 return 0;
2296 }
b873e23a 2297 vector<DNSName> mustRectify;
1325e8a2 2298 unsigned int zoneErrors=0;
ddac7145 2299 for(unsigned int n = 1; n < cmds.size(); ++n) {
b873e23a 2300 DNSName zone(cmds[n]);
7da05748 2301 dk.startTransaction(zone, -1);
f60f4bcd 2302 if(secureZone(dk, zone)) {
d6c16697 2303 mustRectify.push_back(zone);
1325e8a2
PD
2304 } else {
2305 zoneErrors++;
d6c16697 2306 }
7da05748 2307 dk.commitTransaction();
f60f4bcd 2308 }
1d211b1b 2309
b873e23a 2310 for(const auto& zone : mustRectify)
d6c16697 2311 rectifyZone(dk, zone);
1325e8a2
PD
2312
2313 if (zoneErrors) {
2314 return 1;
2315 }
2316 return 0;
1d211b1b 2317 }
fa377773 2318 else if (cmds[0] == "secure-all-zones") {
f84dba01 2319 if (cmds.size() >= 2 && !pdns_iequals(cmds[1], "increase-serial")) {
fd5076c8 2320 cerr << "Syntax: pdnsutil secure-all-zones [increase-serial]"<<endl;
f84dba01
KM
2321 return 0;
2322 }
2323
fa377773
KM
2324 UeberBackend B("default");
2325
fa377773
KM
2326 vector<DomainInfo> domainInfo;
2327 B.getAllDomains(&domainInfo);
2328
f84dba01 2329 unsigned int zonesSecured=0, zoneErrors=0;
ff05fd12 2330 for(DomainInfo di : domainInfo) {
fa377773 2331 if(!dk.isSecuredZone(di.zone)) {
675fa24c 2332 cout<<"Securing "<<di.zone.toString()<<": ";
f84dba01
KM
2333 if (secureZone(dk, di.zone)) {
2334 zonesSecured++;
2335 if (cmds.size() == 2) {
2336 if (!increaseSerial(di.zone, dk))
2337 continue;
2338 } else
2339 continue;
2340 }
2341 zoneErrors++;
fa377773
KM
2342 }
2343 }
fa377773 2344
f84dba01 2345 cout<<"Secured: "<<zonesSecured<<" zones. Errors: "<<zoneErrors<<endl;
fa377773
KM
2346
2347 if (zoneErrors) {
2348 return 1;
2349 }
2350 return 0;
2351 }
bddc5d92 2352 else if(cmds[0]=="set-kind") {
2353 if(cmds.size() != 3) {
2354 cerr<<"Syntax: pdnsutil set-kind ZONE KIND"<<endl;
2355 }
2356 DNSName zone(cmds[1]);
2357 auto kind=DomainInfo::stringToKind(cmds[2]);
2358 exit(setZoneKind(zone, kind));
2359 }
da11ed0e 2360 else if(cmds[0]=="set-nsec3") {
37fd8771 2361 if(cmds.size() < 2) {
fd5076c8 2362 cerr<<"Syntax: pdnsutil set-nsec3 ZONE 'params' [narrow]"<<endl;
37fd8771
BH
2363 return 0;
2364 }
b8adb30d 2365 string nsec3params = cmds.size() > 2 ? cmds[2] : "1 0 1 ab";
22c5aa60 2366 bool narrow = cmds.size() > 3 && cmds[3]=="narrow";
da11ed0e 2367 NSEC3PARAMRecordContent ns3pr(nsec3params);
7649cd71 2368
675fa24c 2369 DNSName zone(cmds[1]);
3155c04a
PL
2370 if (zone.wirelength() > 222) {
2371 cerr<<"Cannot enable NSEC3 for " << zone.toString() << " as it is too long (" << zone.wirelength() << " bytes, maximum is 222 bytes)"<<endl;
2372 return 1;
2373 }
7649cd71
KM
2374 if (! dk.setNSEC3PARAM(zone, ns3pr, narrow)) {
2375 cerr<<"Cannot set NSEC3 param for " << zone.toString() << endl;
2376 return 1;
775acd9e 2377 }
7649cd71 2378
b8adb30d 2379 if (!ns3pr.d_flags)
7649cd71 2380 cerr<<"NSEC3 set, ";
b8adb30d 2381 else
7649cd71
KM
2382 cerr<<"NSEC3 (opt-out) set, ";
2383
2384 if(dk.isSecuredZone(zone))
2385 cerr<<"please rectify your zone if your backend needs it"<<endl;
2386 else
2387 cerr<<"please secure and rectify your zone."<<endl;
2388
2389 return 0;
da11ed0e 2390 }
d3e7090c 2391 else if(cmds[0]=="set-presigned") {
5935cede 2392 if(cmds.size() < 2) {
fd5076c8 2393 cerr<<"Syntax: pdnsutil set-presigned ZONE"<<endl;
37fd8771 2394 return 0;
5935cede 2395 }
b873e23a 2396 if (! dk.setPresigned(DNSName(cmds[1]))) {
e15dce0c 2397 cerr << "Could not set presigned on for " << cmds[1] << endl;
a84a8203
PD
2398 return 1;
2399 }
2400 return 0;
d3e7090c 2401 }
088370cd
PL
2402 else if(cmds[0]=="set-publish-cdnskey") {
2403 if(cmds.size() < 2) {
fd5076c8 2404 cerr<<"Syntax: pdnsutil set-publish-cdnskey ZONE"<<endl;
088370cd
PL
2405 return 0;
2406 }
da347642 2407 if (! dk.setPublishCDNSKEY(DNSName(cmds[1]))) {
088370cd
PL
2408 cerr << "Could not set publishing for CDNSKEY records for "<< cmds[1]<<endl;
2409 return 1;
2410 }
2411 return 0;
2412 }
ef542223
PL
2413 else if(cmds[0]=="set-publish-cds") {
2414 if(cmds.size() < 2) {
fd5076c8 2415 cerr<<"Syntax: pdnsutil set-publish-cds ZONE [DIGESTALGOS]"<<endl;
ef542223
PL
2416 return 0;
2417 }
2418
2419 // If DIGESTALGOS is unset
2420 if(cmds.size() == 2)
2421 cmds.push_back("1,2");
2422
da347642 2423 if (! dk.setPublishCDS(DNSName(cmds[1]), cmds[2])) {
ef542223
PL
2424 cerr << "Could not set publishing for CDS records for "<< cmds[1]<<endl;
2425 return 1;
2426 }
2427 return 0;
2428 }
d3e7090c 2429 else if(cmds[0]=="unset-presigned") {
37fd8771 2430 if(cmds.size() < 2) {
fd5076c8 2431 cerr<<"Syntax: pdnsutil unset-presigned ZONE"<<endl;
f60f4bcd 2432 return 0;
37fd8771 2433 }
b873e23a 2434 if (! dk.unsetPresigned(DNSName(cmds[1]))) {
e15dce0c 2435 cerr << "Could not unset presigned on for " << cmds[1] << endl;
a84a8203
PD
2436 return 1;
2437 }
2438 return 0;
d3e7090c 2439 }
088370cd
PL
2440 else if(cmds[0]=="unset-publish-cdnskey") {
2441 if(cmds.size() < 2) {
fd5076c8 2442 cerr<<"Syntax: pdnsutil unset-publish-cdnskey ZONE"<<endl;
088370cd
PL
2443 return 0;
2444 }
da347642 2445 if (! dk.unsetPublishCDNSKEY(DNSName(cmds[1]))) {
088370cd
PL
2446 cerr << "Could not unset publishing for CDNSKEY records for "<< cmds[1]<<endl;
2447 return 1;
2448 }
2449 return 0;
2450 }
ef542223
PL
2451 else if(cmds[0]=="unset-publish-cds") {
2452 if(cmds.size() < 2) {
fd5076c8 2453 cerr<<"Syntax: pdnsutil unset-publish-cds ZONE"<<endl;
ef542223
PL
2454 return 0;
2455 }
da347642 2456 if (! dk.unsetPublishCDS(DNSName(cmds[1]))) {
ef542223
PL
2457 cerr << "Could not unset publishing for CDS records for "<< cmds[1]<<endl;
2458 return 1;
2459 }
2460 return 0;
2461 }
65c87942
BH
2462 else if(cmds[0]=="hash-zone-record") {
2463 if(cmds.size() < 3) {
fd5076c8 2464 cerr<<"Syntax: pdnsutil hash-zone-record ZONE RNAME"<<endl;
65c87942
BH
2465 return 0;
2466 }
675fa24c 2467 DNSName zone(cmds[1]);
b873e23a 2468 DNSName record(cmds[2]);
65c87942
BH
2469 NSEC3PARAMRecordContent ns3pr;
2470 bool narrow;
2471 if(!dk.getNSEC3PARAM(zone, &ns3pr, &narrow)) {
675fa24c 2472 cerr<<"The '"<<zone.toString()<<"' zone does not use NSEC3"<<endl;
65c87942
BH
2473 return 0;
2474 }
5e42374c 2475 if(narrow) {
675fa24c 2476 cerr<<"The '"<<zone.toString()<<"' zone uses narrow NSEC3, but calculating hash anyhow"<<endl;
65c87942
BH
2477 }
2478
28e2e78e 2479 cout<<toBase32Hex(hashQNameWithSalt(ns3pr, record))<<endl;
65c87942 2480 }
da11ed0e 2481 else if(cmds[0]=="unset-nsec3") {
37fd8771 2482 if(cmds.size() < 2) {
fd5076c8 2483 cerr<<"Syntax: pdnsutil unset-nsec3 ZONE"<<endl;
a84a8203 2484 return 0;
37fd8771 2485 }
b873e23a 2486 if ( ! dk.unsetNSEC3PARAM(DNSName(cmds[1]))) {
e15dce0c 2487 cerr<<"Cannot unset NSEC3 param for " << cmds[1] << endl;
a84a8203
PD
2488 return 1;
2489 }
2490 return 0;
da11ed0e
BH
2491 }
2492 else if(cmds[0]=="export-zone-key") {
7ddd79a7 2493 if(cmds.size() < 3) {
fd5076c8 2494 cerr<<"Syntax: pdnsutil export-zone-key ZONE KEY-ID"<<endl;
a84a8203 2495 return 0;
7ddd79a7
BH
2496 }
2497
da11ed0e 2498 string zone=cmds[1];
335da0ba 2499 unsigned int id=pdns_stou(cmds[2]);
b873e23a 2500 DNSSECPrivateKey dpk=dk.getKeyById(DNSName(zone), id);
189bb9d2 2501 cout << dpk.getKey()->convertToISC() <<endl;
4496f66f 2502 }
04576eed
RA
2503 else if(cmds[0]=="increase-serial") {
2504 if (cmds.size() < 2) {
fd5076c8 2505 cerr<<"Syntax: pdnsutil increase-serial ZONE"<<endl;
04576eed
RA
2506 return 0;
2507 }
b873e23a 2508 return increaseSerial(DNSName(cmds[1]), dk);
04576eed 2509 }
ed3f8559
BH
2510 else if(cmds[0]=="import-zone-key-pem") {
2511 if(cmds.size() < 4) {
fd5076c8 2512 cerr<<"Syntax: pdnsutil import-zone-key-pem ZONE FILE ALGORITHM {ksk|zsk}"<<endl;
ed3f8559
BH
2513 exit(1);
2514 }
2515 string zone=cmds[1];
2516 string fname=cmds[2];
2517 string line;
2518 ifstream ifs(fname.c_str());
2519 string tmp, interim, raw;
2520 while(getline(ifs, line)) {
2521 if(line[0]=='-')
2522 continue;
2523 trim(line);
2524 interim += line;
2525 }
2526 B64Decode(interim, raw);
2527 DNSSECPrivateKey dpk;
699e6e37 2528 DNSKEYRecordContent drc;
8d9f38f2 2529 shared_ptr<DNSCryptoKeyEngine> key(DNSCryptoKeyEngine::makeFromPEMString(drc, raw));
699e6e37 2530 dpk.setKey(key);
ed3f8559 2531
335da0ba 2532 dpk.d_algorithm = pdns_stou(cmds[3]);
ed3f8559
BH
2533
2534 if(dpk.d_algorithm == 7)
2535 dpk.d_algorithm = 5;
2536
2537 cerr<<(int)dpk.d_algorithm<<endl;
2538
2539 if(cmds.size() > 4) {
2540 if(pdns_iequals(cmds[4], "ZSK"))
2541 dpk.d_flags = 256;
2542 else if(pdns_iequals(cmds[4], "KSK"))
2543 dpk.d_flags = 257;
2544 else {
451ba512 2545 cerr<<"Unknown key flag '"<<cmds[4]<<"'"<<endl;
ed3f8559
BH
2546 exit(1);
2547 }
2548 }
2549 else
2550 dpk.d_flags = 257; // ksk
2551
b873e23a 2552 if(!dk.addKey(DNSName(zone), dpk)) {
07bf35d1 2553 cerr<<"Adding key failed, perhaps DNSSEC not enabled in configuration?"<<endl;
2554 exit(1);
2555 }
ed3f8559
BH
2556
2557 }
976b6541 2558 else if(cmds[0]=="import-zone-key") {
d1eacbeb 2559 if(cmds.size() < 3) {
d9db3d7e 2560 cerr<<"Syntax: pdnsutil import-zone-key ZONE FILE [ksk|zsk] [active|inactive]"<<endl;
4496f66f
BH
2561 exit(1);
2562 }
976b6541
BH
2563 string zone=cmds[1];
2564 string fname=cmds[2];
2565 DNSSECPrivateKey dpk;
699e6e37 2566 DNSKEYRecordContent drc;
8d9f38f2 2567 shared_ptr<DNSCryptoKeyEngine> key(DNSCryptoKeyEngine::makeFromISCFile(drc, fname.c_str()));
699e6e37 2568 dpk.setKey(key);
7ddd79a7
BH
2569 dpk.d_algorithm = drc.d_algorithm;
2570
2571 if(dpk.d_algorithm == 7)
2572 dpk.d_algorithm = 5;
aa952078 2573
4af49b80 2574 dpk.d_flags = 257;
4cec6ac5 2575 bool active=true;
4af49b80 2576
2577 for(unsigned int n = 3; n < cmds.size(); ++n) {
2578 if(pdns_iequals(cmds[n], "ZSK"))
232f0877 2579 dpk.d_flags = 256;
4af49b80 2580 else if(pdns_iequals(cmds[n], "KSK"))
232f0877 2581 dpk.d_flags = 257;
4af49b80 2582 else if(pdns_iequals(cmds[n], "active"))
232f0877 2583 active = 1;
d9db3d7e 2584 else if(pdns_iequals(cmds[n], "passive") || pdns_iequals(cmds[n], "inactive")) // passive eventually needs to be removed
232f0877 2585 active = 0;
4af49b80 2586 else {
232f0877
CH
2587 cerr<<"Unknown key flag '"<<cmds[n]<<"'"<<endl;
2588 exit(1);
2589 }
aa952078 2590 }
b873e23a 2591 if(!dk.addKey(DNSName(zone), dpk, active)) {
07bf35d1 2592 cerr<<"Adding key failed, perhaps DNSSEC not enabled in configuration?"<<endl;
2593 exit(1);
2594 }
976b6541 2595 }
da11ed0e 2596 else if(cmds[0]=="export-zone-dnskey") {
7ddd79a7 2597 if(cmds.size() < 3) {
fd5076c8 2598 cerr<<"Syntax: pdnsutil export-zone-dnskey ZONE KEY-ID"<<endl;
7ddd79a7
BH
2599 exit(1);
2600 }
2601
b873e23a 2602 DNSName zone(cmds[1]);
335da0ba 2603 unsigned int id=pdns_stou(cmds[2]);
da11ed0e
BH
2604 DNSSECPrivateKey dpk=dk.getKeyById(zone, id);
2605 cout << zone<<" IN DNSKEY "<<dpk.getDNSKEY().getZoneRepresentation() <<endl;
f8b25763
BH
2606 if(dpk.d_flags == 257) {
2607 cout << zone << " IN DS "<<makeDSFromDNSKey(zone, dpk.getDNSKEY(), 1).getZoneRepresentation() << endl;
2608 cout << zone << " IN DS "<<makeDSFromDNSKey(zone, dpk.getDNSKEY(), 2).getZoneRepresentation() << endl;
2609 }
da11ed0e 2610 }
950bdddf
PD
2611 else if(cmds[0] == "generate-zone-key") {
2612 if(cmds.size() < 2 ) {
fd5076c8 2613 cerr << "Syntax: pdnsutil generate-zone-key zsk|ksk [rsasha1|rsasha256|rsasha512|gost|ecdsa256|ecdsa384] [bits]"<<endl;
950bdddf
PD
2614 return 0;
2615 }
2616 // need to get algorithm, bits & ksk or zsk from commandline
2617 bool keyOrZone=false;
2618 int tmp_algo=0;
2619 int bits=0;
2620 int algorithm=8;
2621 for(unsigned int n=1; n < cmds.size(); ++n) {
2622 if(pdns_iequals(cmds[n], "zsk"))
2623 keyOrZone = false;
2624 else if(pdns_iequals(cmds[n], "ksk"))
2625 keyOrZone = true;
2626 else if((tmp_algo = shorthand2algorithm(cmds[n]))>0) {
2627 algorithm = tmp_algo;
335da0ba
AT
2628 } else if(pdns_stou(cmds[n]))
2629 bits = pdns_stou(cmds[n]);
950bdddf
PD
2630 else {
2631 cerr<<"Unknown algorithm, key flag or size '"<<cmds[n]<<"'"<<endl;
2632 return 0;
2633 }
2634 }
2635 cerr<<"Generating a " << (keyOrZone ? "KSK" : "ZSK")<<" with algorithm = "<<algorithm<<endl;
2636 if(bits)
2637 cerr<<"Requesting specific key size of "<<bits<<" bits"<<endl;
2638
2639 DNSSECPrivateKey dspk;
2640 shared_ptr<DNSCryptoKeyEngine> dpk(DNSCryptoKeyEngine::make(algorithm)); // defaults to RSA for now, could be smart w/algorithm! XXX FIXME
2641 if(!bits) {
2642 if(algorithm <= 10)
2643 bits = keyOrZone ? 2048 : 1024;
2644 else {
2645 if(algorithm == 12 || algorithm == 13 || algorithm == 250) // ECDSA, GOST, ED25519
2646 bits = 256;
2647 else if(algorithm == 14)
2648 bits = 384;
2649 else {
335da0ba 2650 throw runtime_error("Can't guess key size for algorithm "+std::to_string(algorithm));
950bdddf
PD
2651 }
2652 }
2653 }
2654 dpk->create(bits);
2655 dspk.setKey(dpk);
2656 dspk.d_algorithm = algorithm;
2657 dspk.d_flags = keyOrZone ? 257 : 256;
2658
2659 // print key to stdout
2660 cout << "Flags: " << dspk.d_flags << endl <<
2661 dspk.getKey()->convertToISC() << endl;
79b4ea54 2662 } else if (cmds[0]=="generate-tsig-key") {
6f872b78
AT
2663 if (cmds.size() < 3) {
2664 cerr << "Syntax: " << cmds[0] << " name (hmac-md5|hmac-sha1|hmac-sha224|hmac-sha256|hmac-sha384|hmac-sha512)" << endl;
2665 return 0;
2666 }
b873e23a 2667 DNSName name(cmds[1]);
6f872b78
AT
2668 string algo = cmds[2];
2669 string key;
2670 char tmpkey[64];
2671
d885cea7 2672 size_t klen = 0;
6f872b78
AT
2673 if (algo == "hmac-md5") {
2674 klen = 32;
2675 } else if (algo == "hmac-sha1") {
2676 klen = 32;
2677 } else if (algo == "hmac-sha224") {
2678 klen = 32;
2679 } else if (algo == "hmac-sha256") {
2680 klen = 64;
2681 } else if (algo == "hmac-sha384") {
2682 klen = 64;
2683 } else if (algo == "hmac-sha512") {
2684 klen = 64;
d885cea7
AT
2685 } else {
2686 cerr << "Cannot generate key for " << algo << endl;
2687 return 1;
6f872b78
AT
2688 }
2689
7e5b2860 2690 cerr << "Generating new key with " << klen << " bytes (this can take a while)" << endl;
a56bc64d
AT
2691 for(size_t i = 0; i < klen; i+=4) {
2692 *(unsigned int*)(tmpkey+i) = dns_random(0xffffffff);
2693 }
6f872b78
AT
2694 key = Base64Encode(std::string(tmpkey, klen));
2695
2696 UeberBackend B("default");
b873e23a 2697 if (B.setTSIGKey(name, DNSName(algo), key)) { // you are feeling bored, put up DNSName(algo) up earlier
6f872b78
AT
2698 cout << "Create new TSIG key " << name << " " << algo << " " << key << endl;
2699 } else {
694ea7ae 2700 cerr << "Failure storing new TSIG key " << name << " " << algo << " " << key << endl;
6f872b78
AT
2701 return 1;
2702 }
2703 return 0;
2704 } else if (cmds[0]=="import-tsig-key") {
2705 if (cmds.size() < 4) {
2706 cerr << "Syntax: " << cmds[0] << " name algorithm key" << endl;
2707 return 0;
2708 }
b873e23a 2709 DNSName name(cmds[1]);
6f872b78
AT
2710 string algo = cmds[2];
2711 string key = cmds[3];
2712
2713 UeberBackend B("default");
b873e23a 2714 if (B.setTSIGKey(name, DNSName(algo), key)) {
6f872b78
AT
2715 cout << "Imported TSIG key " << name << " " << algo << endl;
2716 } else {
694ea7ae 2717 cerr << "Failure importing TSIG key " << name << " " << algo << endl;
6f872b78
AT
2718 return 1;
2719 }
2720 return 0;
2721 } else if (cmds[0]=="delete-tsig-key") {
2722 if (cmds.size() < 2) {
2723 cerr << "Syntax: " << cmds[0] << " name" << endl;
2724 return 0;
2725 }
b873e23a 2726 DNSName name(cmds[1]);
6f872b78
AT
2727
2728 UeberBackend B("default");
2729 if (B.deleteTSIGKey(name)) {
2730 cout << "Deleted TSIG key " << name << endl;
2731 } else {
694ea7ae 2732 cerr << "Failure deleting TSIG key " << name << endl;
6f872b78
AT
2733 return 1;
2734 }
2735 return 0;
2736 } else if (cmds[0]=="list-tsig-keys") {
2737 std::vector<struct TSIGKey> keys;
2738 UeberBackend B("default");
2739 if (B.getTSIGKeys(keys)) {
ff05fd12 2740 for(const struct TSIGKey &key : keys) {
675fa24c 2741 cout << key.name.toString() << " " << key.algorithm.toString() << " " << key.key << endl;
6f872b78
AT
2742 }
2743 }
2744 return 0;
79b4ea54 2745 } else if (cmds[0]=="activate-tsig-key") {
4831df57
AT
2746 string metaKey;
2747 if (cmds.size() < 4) {
ec53ae22 2748 cerr << "Syntax: " << cmds[0] << " ZONE NAME {master|slave}" << endl;
6f872b78
AT
2749 return 0;
2750 }
b873e23a 2751 DNSName zname(cmds[1]);
6f872b78 2752 string name = cmds[2];
4831df57
AT
2753 if (cmds[3] == "master")
2754 metaKey = "TSIG-ALLOW-AXFR";
2755 else if (cmds[3] == "slave")
2756 metaKey = "AXFR-MASTER-TSIG";
2757 else {
2758 cerr << "Invalid parameter '" << cmds[3] << "', expected master or slave" << endl;
2759 return 1;
2760 }
6f872b78
AT
2761 UeberBackend B("default");
2762 std::vector<std::string> meta;
4831df57 2763 if (!B.getDomainMetadata(zname, metaKey, meta)) {
694ea7ae 2764 cerr << "Failure enabling TSIG key " << name << " for " << zname << endl;
6f872b78
AT
2765 return 1;
2766 }
2767 bool found = false;
ff05fd12 2768 for(std::string tmpname : meta) {
6f872b78
AT
2769 if (tmpname == name) { found = true; break; }
2770 }
2771 if (!found) meta.push_back(name);
4831df57 2772 if (B.setDomainMetadata(zname, metaKey, meta)) {
6f872b78
AT
2773 cout << "Enabled TSIG key " << name << " for " << zname << endl;
2774 } else {
694ea7ae 2775 cerr << "Failure enabling TSIG key " << name << " for " << zname << endl;
6f872b78
AT
2776 return 1;
2777 }
2778 return 0;
79b4ea54 2779 } else if (cmds[0]=="deactivate-tsig-key") {
4831df57
AT
2780 string metaKey;
2781 if (cmds.size() < 4) {
ec53ae22 2782 cerr << "Syntax: " << cmds[0] << " ZONE NAME {master|slave}" << endl;
6f872b78
AT
2783 return 0;
2784 }
b873e23a 2785 DNSName zname(cmds[1]);
6f872b78 2786 string name = cmds[2];
4831df57
AT
2787 if (cmds[3] == "master")
2788 metaKey = "TSIG-ALLOW-AXFR";
2789 else if (cmds[3] == "slave")
2790 metaKey = "AXFR-MASTER-TSIG";
2791 else {
2792 cerr << "Invalid parameter '" << cmds[3] << "', expected master or slave" << endl;
2793 return 1;
2794 }
6f872b78
AT
2795
2796 UeberBackend B("default");
2797 std::vector<std::string> meta;
4831df57 2798 if (!B.getDomainMetadata(zname, metaKey, meta)) {
694ea7ae 2799 cerr << "Failure disabling TSIG key " << name << " for " << zname << endl;
6f872b78
AT
2800 return 1;
2801 }
2802 std::vector<std::string>::iterator iter = meta.begin();
2803 for(;iter != meta.end(); iter++) if (*iter == name) break;
2804 if (iter != meta.end()) meta.erase(iter);
4831df57 2805 if (B.setDomainMetadata(zname, metaKey, meta)) {
6f872b78
AT
2806 cout << "Disabled TSIG key " << name << " for " << zname << endl;
2807 } else {
694ea7ae 2808 cerr << "Failure disabling TSIG key " << name << " for " << zname << endl;
6f872b78
AT
2809 return 1;
2810 }
2811 return 0;
451ba512
AT
2812 } else if (cmds[0]=="get-meta") {
2813 UeberBackend B("default");
2814 if (cmds.size() < 2) {
2815 cerr << "Syntax: " << cmds[0] << " zone [kind kind ..]" << endl;
2816 return 1;
2817 }
b873e23a 2818 DNSName zone(cmds[1]);
451ba512
AT
2819 vector<string> keys;
2820 DomainInfo di;
2821
2822 if (!B.getDomainInfo(zone, di)) {
2823 cerr << "Invalid zone '" << zone << "'" << endl;
2824 return 1;
2825 }
2826
2827 if (cmds.size() > 2) {
2e050d2c
AT
2828 keys.assign(cmds.begin() + 2, cmds.end());
2829 std::cout << "Metadata for '" << zone << "'" << endl;
ff05fd12 2830 for(const string kind : keys) {
2e050d2c
AT
2831 vector<string> meta;
2832 meta.clear();
2833 if (B.getDomainMetadata(zone, kind, meta)) {
2834 cout << kind << " = " << boost::join(meta, ", ") << endl;
2835 }
2836 }
451ba512 2837 } else {
2e050d2c
AT
2838 std::map<std::string, std::vector<std::string> > meta;
2839 std::cout << "Metadata for '" << zone << "'" << endl;
2840 B.getAllDomainMetadata(zone, meta);
2841 for(std::map<std::string, std::vector<std::string> >::const_iterator each_meta = meta.begin(); each_meta != meta.end(); each_meta++) {
2842 cout << each_meta->first << " = " << boost::join(each_meta->second, ", ") << endl;
451ba512 2843 }
2e050d2c
AT
2844 }
2845 return 0;
2846
451ba512
AT
2847 } else if (cmds[0]=="set-meta") {
2848 UeberBackend B("default");
2849 if (cmds.size() < 3) {
2850 cerr << "Syntax: " << cmds[0] << " zone kind [value value ..]" << endl;
2851 return 1;
2852 }
b873e23a 2853 DNSName zone(cmds[1]);
451ba512
AT
2854 string kind = cmds[2];
2855 vector<string> meta(cmds.begin() + 3, cmds.end());
2856
2857 if (!B.setDomainMetadata(zone, kind, meta)) {
2858 cerr << "Unable to set meta for '" << zone << "'" << endl;
2859 return 1;
2860 } else {
b9d63cb9 2861 cout << "Set '" << zone.toStringNoDot() << "' meta " << kind << " = " << boost::join(meta, ", ") << endl;
451ba512 2862 }
8daea594 2863 } else if (cmds[0]=="hsm") {
3c12a7e9 2864#ifdef HAVE_P11KIT1
8daea594
AT
2865 UeberBackend B("default");
2866 if (cmds[1] == "assign") {
2867 DNSCryptoKeyEngine::storvector_t storvect;
2868 DomainInfo di;
629b6616 2869 std::vector<DNSBackend::KeyData> keys;
829035c1
AT
2870
2871 if (cmds.size() < 9) {
fd5076c8 2872 std::cout << "Usage: pdnsutil hsm assign ZONE ALGORITHM {ksk|zsk} MODULE TOKEN PIN LABEL" << std::endl;
829035c1
AT
2873 return 1;
2874 }
2875
e8afb726 2876 DNSName zone(cmds[2]);
829035c1 2877
8daea594
AT
2878 // verify zone
2879 if (!B.getDomainInfo(zone, di)) {
2880 cerr << "Unable to assign module to unknown zone '" << zone << "'" << std::endl;
2881 return 1;
2882 }
2883
2884 int algorithm = shorthand2algorithm(cmds[3]);
5dce7548
AT
2885 if (algorithm<0) {
2886 cerr << "Unable to use unknown algorithm '" << cmds[3] << "'" << std::endl;
2887 return 1;
2888 }
2889
829035c1 2890 int id;
8daea594
AT
2891 bool keyOrZone = (cmds[4] == "ksk" ? true : false);
2892 string module = cmds[5];
2893 string slot = cmds[6];
2894 string pin = cmds[7];
2895 string label = cmds[8];
2896
2897 std::ostringstream iscString;
2898 iscString << "Private-key-format: v1.2" << std::endl <<
2899 "Algorithm: " << algorithm << std::endl <<
2900 "Engine: " << module << std::endl <<
2901 "Slot: " << slot << std::endl <<
2902 "PIN: " << pin << std::endl <<
2903 "Label: " << label << std::endl;
2904
2905 DNSKEYRecordContent drc;
2906 DNSSECPrivateKey dpk;
2907 dpk.d_flags = (keyOrZone ? 257 : 256);
2908 dpk.setKey(shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromISCString(drc, iscString.str())));
829035c1 2909
629b6616
AT
2910 // make sure this key isn't being reused.
2911 B.getDomainKeys(zone, 0, keys);
2912 id = -1;
2913
ff05fd12 2914 for(DNSBackend::KeyData& kd : keys) {
629b6616
AT
2915 if (kd.content == iscString.str()) {
2916 // it's this one, I guess...
2917 id = kd.id;
2918 break;
2919 }
2920 }
2921
2922 if (id > -1) {
2923 cerr << "You have already assigned this key with ID=" << id << std::endl;
2924 return 1;
2925 }
2926
829035c1 2927 if (!(id = dk.addKey(zone, dpk))) {
8daea594
AT
2928 cerr << "Unable to assign module slot to zone" << std::endl;
2929 return 1;
2930 }
2931
86c979f5
AT
2932 // figure out key id.
2933
86c979f5
AT
2934 B.getDomainKeys(zone, 0, keys);
2935
2936 // validate which one got the key...
ff05fd12 2937 for(DNSBackend::KeyData& kd : keys) {
86c979f5
AT
2938 if (kd.content == iscString.str()) {
2939 // it's this one, I guess...
2940 id = kd.id;
2941 break;
2942 }
2943 }
2944
829035c1 2945 cerr << "Module " << module << " slot " << slot << " assigned to " << zone << " with key id " << id << endl;
86c979f5 2946
8daea594
AT
2947 return 0;
2948 } else if (cmds[1] == "create-key") {
70f0f8c4
AT
2949
2950 if (cmds.size() < 4) {
fd5076c8 2951 cerr << "Usage: pdnsutil hsm create-key ZONE KEY-ID [BITS]" << endl;
70f0f8c4
AT
2952 return 1;
2953 }
8daea594 2954 DomainInfo di;
e8afb726 2955 DNSName zone(cmds[2]);
8daea594 2956 unsigned int id;
70f0f8c4 2957 int bits = 2048;
8daea594
AT
2958 // verify zone
2959 if (!B.getDomainInfo(zone, di)) {
2960 cerr << "Unable to create key for unknown zone '" << zone << "'" << std::endl;
2961 return 1;
2962 }
2963
335da0ba 2964 id = pdns_stou(cmds[3]);
8daea594
AT
2965 std::vector<DNSBackend::KeyData> keys;
2966 if (!B.getDomainKeys(zone, 0, keys)) {
2967 cerr << "No keys found for zone " << zone << std::endl;
2968 return 1;
2969 }
2970
2971 DNSCryptoKeyEngine *dke = NULL;
2972 // lookup correct key
ff05fd12 2973 for(DNSBackend::KeyData &kd : keys) {
8daea594
AT
2974 if (kd.id == id) {
2975 // found our key.
2976 DNSKEYRecordContent dkrc;
2977 dke = DNSCryptoKeyEngine::makeFromISCString(dkrc, kd.content);
2978 }
2979 }
2980
2981 if (!dke) {
2982 cerr << "Could not find key with ID " << id << endl;
2983 return 1;
2984 }
70f0f8c4 2985 if (cmds.size() > 4) {
335da0ba 2986 bits = pdns_stou(cmds[4]);
70f0f8c4
AT
2987 }
2988 if (bits < 1) {
2989 cerr << "Invalid bit size " << bits << "given, must be positive integer";
2990 return 1;
2991 }
829035c1 2992 try {
70f0f8c4
AT
2993 dke->create(bits);
2994 } catch (PDNSException& e) {
2995 cerr << e.reason << endl;
829035c1
AT
2996 return 1;
2997 }
8daea594 2998
70f0f8c4 2999 cerr << "Key of size " << bits << " created" << std::endl;
8daea594
AT
3000 return 0;
3001 }
3c12a7e9
AT
3002#else
3003 cerr<<"PKCS#11 support not enabled"<<endl;
3004 return 1;
3005#endif
90c2c8e9
AT
3006 } else if (cmds[0] == "b2b-migrate") {
3007 if (cmds.size() < 3) {
ec53ae22 3008 cerr<<"Usage: b2b-migrate OLD NEW"<<endl;
90c2c8e9
AT
3009 return 1;
3010 }
3011
3012 DNSBackend *src,*tgt;
3013 src = tgt = NULL;
3014
3015 for(DNSBackend *b : BackendMakers().all()) {
3016 if (b->getPrefix() == cmds[1]) src = b;
3017 if (b->getPrefix() == cmds[2]) tgt = b;
3018 }
3019 if (!src) {
3020 cerr<<"Unknown source backend '"<<cmds[1]<<"'"<<endl;
3021 return 1;
3022 }
3023 if (!tgt) {
3024 cerr<<"Unknown target backend '"<<cmds[2]<<"'"<<endl;
3025 return 1;
3026 }
3027
3028 cout<<"Moving zone(s) from "<<src->getPrefix()<<" to "<<tgt->getPrefix()<<endl;
3029
3030 vector<DomainInfo> domains;
3031
3032 tgt->getAllDomains(&domains, true);
3033 if (domains.size()>0)
3034 throw PDNSException("Target backend has domain(s), please clean it first");
3035
3036 src->getAllDomains(&domains, true);
3037 // iterate zones
3038 for(const DomainInfo& di: domains) {
3039 size_t nr,nc,nm,nk;
3040 DNSResourceRecord rr;
21a3792f 3041 cout<<"Processing '"<<di.zone.toString()<<"'"<<endl;
90c2c8e9
AT
3042 // create zone
3043 if (!tgt->createDomain(di.zone)) throw PDNSException("Failed to create zone");
3044 tgt->setKind(di.zone, di.kind);
3045 tgt->setAccount(di.zone,di.account);
3046 for(const string& master: di.masters) {
3047 tgt->setMaster(di.zone, master);
3048 }
3049 // move records
3050 if (!src->list(di.zone, di.id, true)) throw PDNSException("Failed to list records");
3051 nr=0;
3052 while(src->get(rr)) {
3053 if (!tgt->feedRecord(rr)) throw PDNSException("Failed to feed record");
3054 nr++;
3055 }
3056 // move comments
3057 nc=0;
3058 if (src->listComments(di.id)) {
3059 Comment c;
3060 while(src->getComment(c)) {
3061 tgt->feedComment(c);
3062 nc++;
3063 }
3064 }
3065 // move metadata
3066 nm=0;
3067 std::map<std::string, std::vector<std::string> > meta;
3068 if (src->getAllDomainMetadata(di.zone, meta)) {
3069 std::map<std::string, std::vector<std::string> >::iterator i;
3070 for(i=meta.begin(); i != meta.end(); i++) {
3071 if (!tgt->setDomainMetadata(di.zone, i->first, i->second)) throw PDNSException("Failed to feed domain metadata");
3072 nm++;
3073 }
3074 }
3075 // move keys
3076 nk=0;
3077 std::vector<DNSBackend::KeyData> keys;
3078 if (src->getDomainKeys(di.zone, 0, keys)) {
3079 for(const DNSBackend::KeyData& k: keys) {
3080 tgt->addDomainKey(di.zone, k);
3081 nk++;
3082 }
3083 }
3084 cout<<"Moved "<<nr<<" record(s), "<<nc<<" comment(s), "<<nm<<" metadata(s) and "<<nk<<" cryptokey(s)"<<endl;
3085 }
3086
3087 int ntk=0;
3088 // move tsig keys
3089 std::vector<struct TSIGKey> tkeys;
3090 if (src->getTSIGKeys(tkeys)) {
3091 for(const struct TSIGKey& tk: tkeys) {
3092 if (!tgt->setTSIGKey(tk.name, tk.algorithm, tk.key)) throw PDNSException("Failed to feed TSIG key");
3093 ntk++;
3094 }
3095 }
3096 cout<<"Moved "<<ntk<<" TSIG key(s)"<<endl;
3097
3098 cout<<"Remember to drop the old backend and run rectify-all-zones"<<endl;
3099
f641c3b7
PD
3100 return 0;
3101 } else if (cmds[0] == "backend-cmd") {
3102 if (cmds.size() < 3) {
3103 cerr<<"Usage: backend-cmd BACKEND CMD [CMD..]"<<endl;
3104 return 1;
3105 }
3106
3107 DNSBackend *db;
3108 db = NULL;
3109
3110 for(DNSBackend *b : BackendMakers().all()) {
3111 if (b->getPrefix() == cmds[1]) db = b;
3112 }
3113
3114 if (!db) {
3115 cerr<<"Unknown backend '"<<cmds[1]<<"'"<<endl;
3116 return 1;
3117 }
3118
3119 for(auto i=next(begin(cmds),2); i != end(cmds); ++i) {
3120 cerr<<"== "<<*i<<endl;
3121 cout<<db->directBackendCmd(*i);
3122 }
3123
90c2c8e9 3124 return 0;
451ba512 3125 } else {
5cde65f4 3126 cerr<<"Unknown command '"<<cmds[0] <<"'"<< endl;
1d211b1b
BH
3127 return 1;
3128 }
3129 return 0;
3130}
3f81d239 3131catch(PDNSException& ae) {
20002664 3132 cerr<<"Error: "<<ae.reason<<endl;
0c4c552d 3133 return 1;
20002664 3134}
14133ba3
BH
3135catch(std::exception& e) {
3136 cerr<<"Error: "<<e.what()<<endl;
0c4c552d 3137 return 1;
14133ba3 3138}
38392ad7 3139catch(...)
3140{
3141 cerr<<"Caught an unknown exception"<<endl;
3142 return 1;
3143}