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