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