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