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