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