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