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