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