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