]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/pdnssec.cc
snap
[thirdparty/pdns.git] / pdns / pdnssec.cc
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4 #include "dnsseckeeper.hh"
5 #include "dnssecinfra.hh"
6 #include "statbag.hh"
7 #include "base32.hh"
8 #include "base64.hh"
9 #include <boost/foreach.hpp>
10 #include <boost/program_options.hpp>
11 #include <boost/assign/std/vector.hpp>
12 #include <boost/assign/list_of.hpp>
13 #include "dnsbackend.hh"
14 #include "ueberbackend.hh"
15 #include "arguments.hh"
16 #include "packetcache.hh"
17 #include "zoneparser-tng.hh"
18 #include "signingpipe.hh"
19 #include "dns_random.hh"
20 #include <fstream>
21 #ifdef HAVE_SQLITE3
22 #include "ssqlite3.hh"
23 #include "bind-dnssec.schema.sqlite3.sql.h"
24 #endif
25
26 StatBag S;
27 PacketCache PC;
28
29 namespace po = boost::program_options;
30 po::variables_map g_vm;
31
32 string s_programname="pdns";
33
34 namespace {
35 bool g_verbose;
36 }
37
38 ArgvMap &arg()
39 {
40 static ArgvMap arg;
41 return arg;
42 }
43
44 string humanTime(time_t t)
45 {
46 char ret[256];
47 struct tm tm;
48 localtime_r(&t, &tm);
49 strftime(ret, sizeof(ret)-1, "%c", &tm); // %h:%M %Y-%m-%d
50 return ret;
51 }
52
53 static void algorithm2name(uint8_t algo, string &name) {
54 switch(algo) {
55 case 0:
56 name = "Reserved"; return;
57 case 1:
58 name = "RSAMD5"; return;
59 case 2:
60 name = "DH"; return;
61 case 3:
62 name = "DSA"; return;
63 case 4:
64 name = "ECC"; return;
65 case 5:
66 name = "RSASHA1"; return;
67 case 6:
68 name = "DSA-NSEC3-SHA1"; return;
69 case 7:
70 name = "RSASHA1-NSEC3-SHA1"; return;
71 case 8:
72 name = "RSASHA256"; return;
73 case 9:
74 name = "Reserved"; return;
75 case 10:
76 name = "RSASHA512"; return;
77 case 11:
78 name = "Reserved"; return;
79 case 12:
80 name = "ECC-GOST"; return;
81 case 13:
82 name = "ECDSAP256SHA256"; return;
83 case 14:
84 name = "ECDSAP384SHA384"; return;
85 case 252:
86 name = "INDIRECT"; return;
87 case 253:
88 name = "PRIVATEDNS"; return;
89 case 254:
90 name = "PRIVATEOID"; return;
91 default:
92 name = "Unallocated/Reserved"; return;
93 }
94 };
95
96 static int shorthand2algorithm(const string &algorithm)
97 {
98 if (!algorithm.compare("rsamd5")) return 1;
99 if (!algorithm.compare("dh")) return 2;
100 if (!algorithm.compare("dsa")) return 3;
101 if (!algorithm.compare("ecc")) return 4;
102 if (!algorithm.compare("rsasha1")) return 5;
103 if (!algorithm.compare("rsasha256")) return 8;
104 if (!algorithm.compare("rsasha512")) return 10;
105 if (!algorithm.compare("gost")) return 12;
106 if (!algorithm.compare("ecdsa256")) return 13;
107 if (!algorithm.compare("ecdsa384")) return 14;
108 if (!algorithm.compare("ed25519")) return 250;
109 return -1;
110 }
111
112 void loadMainConfig(const std::string& configdir)
113 {
114 ::arg().set("config-dir","Location of configuration directory (pdns.conf)")=configdir;
115 ::arg().set("pipebackend-abi-version","Version of the pipe backend ABI")="1";
116 ::arg().set("default-ttl","Seconds a result is valid if not set otherwise")="3600";
117 ::arg().set("launch","Which backends to launch");
118 ::arg().set("dnssec","if we should do dnssec")="true";
119 ::arg().set("config-name","Name of this virtual configuration - will rename the binary image")=g_vm["config-name"].as<string>();
120 ::arg().setCmd("help","Provide a helpful message");
121 //::arg().laxParse(argc,argv);
122
123 if(::arg().mustDo("help")) {
124 cout<<"syntax:"<<endl<<endl;
125 cout<<::arg().helpstring(::arg()["help"])<<endl;
126 exit(0);
127 }
128
129 if(::arg()["config-name"]!="")
130 s_programname+="-"+::arg()["config-name"];
131
132 string configname=::arg()["config-dir"]+"/"+s_programname+".conf";
133 cleanSlashes(configname);
134
135 ::arg().set("default-ksk-algorithms","Default KSK algorithms")="rsasha256";
136 ::arg().set("default-ksk-size","Default KSK size (0 means default)")="0";
137 ::arg().set("default-zsk-algorithms","Default ZSK algorithms")="rsasha256";
138 ::arg().set("default-zsk-size","Default ZSK size (0 means default)")="0";
139 ::arg().set("max-ent-entries", "Maximum number of empty non-terminals in a zone")="100000";
140 ::arg().set("module-dir","Default directory for modules")=PKGLIBDIR;
141 ::arg().set("entropy-source", "If set, read entropy from this file")="/dev/urandom";
142 ::arg().setSwitch("query-logging","Hint backends that queries should be logged")="no";
143 ::arg().set("loglevel","Amount of logging. Higher is more.")="0";
144 ::arg().setSwitch("direct-dnskey","Fetch DNSKEY RRs from backend during DNSKEY synthesis")="no";
145 ::arg().set("max-nsec3-iterations","Limit the number of NSEC3 hash iterations")="500"; // RFC5155 10.3
146 ::arg().set("max-signature-cache-entries", "Maximum number of signatures cache entries")="";
147 ::arg().laxFile(configname.c_str());
148
149 L.toConsole(Logger::Error); // so we print any errors
150 BackendMakers().launch(::arg()["launch"]); // vrooooom!
151 L.toConsole((Logger::Urgency)(::arg().asNum("loglevel")));
152 ::arg().laxFile(configname.c_str());
153 //cerr<<"Backend: "<<::arg()["launch"]<<", '" << ::arg()["gmysql-dbname"] <<"'" <<endl;
154
155 S.declare("qsize-q","Number of questions waiting for database attention");
156
157 S.declare("deferred-cache-inserts","Amount of cache inserts that were deferred because of maintenance");
158 S.declare("deferred-cache-lookup","Amount of cache lookups that were deferred because of maintenance");
159
160 S.declare("query-cache-hit","Number of hits on the query cache");
161 S.declare("query-cache-miss","Number of misses on the query cache");
162 ::arg().set("max-cache-entries", "Maximum number of cache entries")="1000000";
163 ::arg().set("recursor","If recursion is desired, IP address of a recursing nameserver")="no";
164 ::arg().set("recursive-cache-ttl","Seconds to store packets for recursive queries in the PacketCache")="10";
165 ::arg().set("cache-ttl","Seconds to store packets in the PacketCache")="20";
166 ::arg().set("negquery-cache-ttl","Seconds to store negative query results in the QueryCache")="60";
167 ::arg().set("query-cache-ttl","Seconds to store query results in the QueryCache")="20";
168 ::arg().set("default-soa-name","name to insert in the SOA record if none set in the backend")="a.misconfigured.powerdns.server";
169 ::arg().set("default-soa-mail","mail address to insert in the SOA record if none set in the backend")="";
170 ::arg().set("soa-refresh-default","Default SOA refresh")="10800";
171 ::arg().set("soa-retry-default","Default SOA retry")="3600";
172 ::arg().set("soa-expire-default","Default SOA expire")="604800";
173 ::arg().set("soa-minimum-ttl","Default SOA minimum ttl")="3600";
174
175 UeberBackend::go();
176 }
177
178 // irritatingly enough, rectifyZone needs its own ueberbackend and can't therefore benefit from transactions outside its scope
179 // I think this has to do with interlocking transactions between B and DK, but unsure.
180 bool rectifyZone(DNSSECKeeper& dk, const DNSName& zone)
181 {
182 if(dk.isPresigned(zone)){
183 cerr<<"Rectify presigned zone '"<<zone.toString()<<"' is not allowed/necessary."<<endl;
184 return false;
185 }
186
187 UeberBackend B("default");
188 bool doTransaction=true; // but see above
189 SOAData sd;
190
191 if(!B.getSOAUncached(zone, sd)) {
192 cerr<<"No SOA known for '"<<zone.toString()<<"', is such a zone in the database?"<<endl;
193 return false;
194 }
195 sd.db->list(zone, sd.domain_id);
196
197 DNSResourceRecord rr;
198 set<DNSName> qnames, nsset, dsnames, insnonterm, delnonterm;
199 map<DNSName,bool> nonterm;
200 bool doent=true;
201
202 while(sd.db->get(rr)) {
203 if (rr.qtype.getCode())
204 {
205 qnames.insert(rr.qname);
206 if(rr.qtype.getCode() == QType::NS && rr.qname!=zone)
207 nsset.insert(rr.qname);
208 if(rr.qtype.getCode() == QType::DS)
209 dsnames.insert(rr.qname);
210 }
211 else
212 if(doent)
213 delnonterm.insert(rr.qname);
214 }
215
216 NSEC3PARAMRecordContent ns3pr;
217 bool narrow;
218 bool haveNSEC3=dk.getNSEC3PARAM(zone, &ns3pr, &narrow);
219 bool isOptOut=(haveNSEC3 && ns3pr.d_flags);
220 if(sd.db->doesDNSSEC())
221 {
222 if(!haveNSEC3)
223 cerr<<"Adding NSEC ordering information "<<endl;
224 else if(!narrow) {
225 if(!isOptOut)
226 cerr<<"Adding NSEC3 hashed ordering information for '"<<zone.toString()<<"'"<<endl;
227 else
228 cerr<<"Adding NSEC3 opt-out hashed ordering information for '"<<zone.toString()<<"'"<<endl;
229 } else
230 cerr<<"Erasing NSEC3 ordering since we are narrow, only setting 'auth' fields"<<endl;
231 }
232 else
233 cerr<<"Non DNSSEC zone, only adding empty non-terminals"<<endl;
234
235 if(doTransaction)
236 sd.db->startTransaction("", -1);
237
238 bool realrr=true;
239 string hashed;
240
241 uint32_t maxent = ::arg().asNum("max-ent-entries");
242
243 dononterm:;
244 BOOST_FOREACH(const DNSName& qname, qnames)
245 {
246 bool auth=true;
247 DNSName shorter(qname);
248
249 if(realrr) {
250 do {
251 if(nsset.count(shorter)) {
252 auth=false;
253 break;
254 }
255 } while(shorter.chopOff());
256 }
257
258 if(haveNSEC3)
259 {
260 if(!narrow && (realrr || !isOptOut || nonterm.find(qname)->second)) {
261 hashed=toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, qname));
262 if(g_verbose)
263 cerr<<"'"<<qname.toString()<<"' -> '"<< hashed <<"'"<<endl;
264 sd.db->updateDNSSECOrderAndAuthAbsolute(sd.domain_id, qname, hashed, auth);
265 }
266 else {
267 if(!realrr)
268 auth=false;
269 sd.db->nullifyDNSSECOrderNameAndUpdateAuth(sd.domain_id, qname, auth);
270 }
271 }
272 else // NSEC
273 {
274 sd.db->updateDNSSECOrderAndAuth(sd.domain_id, zone, qname, auth);
275 if (!realrr)
276 sd.db->nullifyDNSSECOrderNameAndUpdateAuth(sd.domain_id, qname, auth);
277 }
278
279 if(realrr)
280 {
281 if (dsnames.count(qname))
282 sd.db->setDNSSECAuthOnDsRecord(sd.domain_id, qname);
283 if (!auth || nsset.count(qname)) {
284 if(isOptOut)
285 sd.db->nullifyDNSSECOrderNameAndAuth(sd.domain_id, qname, "NS");
286 sd.db->nullifyDNSSECOrderNameAndAuth(sd.domain_id, qname, "A");
287 sd.db->nullifyDNSSECOrderNameAndAuth(sd.domain_id, qname, "AAAA");
288 }
289
290 if(doent)
291 {
292 shorter=qname;
293 while(shorter!=zone && shorter.chopOff())
294 {
295 if(!qnames.count(shorter))
296 {
297 if(!(maxent))
298 {
299 cerr<<"Zone '"<<zone.toString()<<"' has too many empty non terminals."<<endl;
300 insnonterm.clear();
301 delnonterm.clear();
302 doent=false;
303 break;
304 }
305
306 if (!delnonterm.count(shorter) && !nonterm.count(shorter))
307 insnonterm.insert(shorter);
308 else
309 delnonterm.erase(shorter);
310
311 if (!nonterm.count(shorter)) {
312 nonterm.insert(pair<DNSName, bool>(shorter, auth));
313 --maxent;
314 } else if (auth)
315 nonterm[shorter]=true;
316 }
317 }
318 }
319 }
320 }
321
322 if(realrr)
323 {
324 //cerr<<"Total: "<<nonterm.size()<<" Insert: "<<insnonterm.size()<<" Delete: "<<delnonterm.size()<<endl;
325 if(!insnonterm.empty() || !delnonterm.empty() || !doent)
326 {
327 sd.db->updateEmptyNonTerminals(sd.domain_id, zone, insnonterm, delnonterm, !doent);
328 }
329 if(doent)
330 {
331 realrr=false;
332 qnames.clear();
333 pair<DNSName,bool> nt;
334 BOOST_FOREACH(nt, nonterm){
335 qnames.insert(nt.first);
336 }
337 goto dononterm;
338 }
339 }
340
341 if(doTransaction)
342 sd.db->commitTransaction();
343
344 return true;
345 }
346
347 void dbBench(const std::string& fname)
348 {
349 ::arg().set("query-cache-ttl")="0";
350 ::arg().set("negquery-cache-ttl")="0";
351 UeberBackend B("default");
352
353 vector<string> domains;
354 if(!fname.empty()) {
355 ifstream ifs(fname.c_str());
356 if(!ifs) {
357 cerr<<"Could not open '"<<fname<<"' for reading domain names to query"<<endl;
358 }
359 string line;
360 while(getline(ifs,line)) {
361 trim(line);
362 domains.push_back(line);
363 }
364 }
365 if(domains.empty())
366 domains.push_back("powerdns.com");
367
368 int n=0;
369 DNSResourceRecord rr;
370 DTime dt;
371 dt.set();
372 unsigned int hits=0, misses=0;
373 for(; n < 10000; ++n) {
374 const string& domain = domains[random() % domains.size()];
375 B.lookup(QType(QType::NS), domain);
376 while(B.get(rr)) {
377 hits++;
378 }
379 B.lookup(QType(QType::A), boost::lexical_cast<string>(random())+"."+domain);
380 while(B.get(rr)) {
381 }
382 misses++;
383
384 }
385 cout<<0.001*dt.udiff()/n<<" millisecond/lookup"<<endl;
386 cout<<"Retrieved "<<hits<<" records, did "<<misses<<" queries which should have no match"<<endl;
387 cout<<"Packet cache reports: "<<S.read("query-cache-hit")<<" hits (should be 0) and "<<S.read("query-cache-miss") <<" misses"<<endl;
388 }
389
390 void rectifyAllZones(DNSSECKeeper &dk)
391 {
392 UeberBackend B("default");
393 vector<DomainInfo> domainInfo;
394
395 B.getAllDomains(&domainInfo);
396 BOOST_FOREACH(DomainInfo di, domainInfo) {
397 cerr<<"Rectifying "<<di.zone.toString()<<": ";
398 rectifyZone(dk, di.zone);
399 }
400 cout<<"Rectified "<<domainInfo.size()<<" zones."<<endl;
401 }
402
403 int checkZone(DNSSECKeeper &dk, UeberBackend &B, const DNSName& zone)
404 {
405 SOAData sd;
406 if(!B.getSOAUncached(zone, sd)) {
407 cout<<"[error] No SOA record present, or active, in zone '"<<zone.toString()<<"'"<<endl;
408 cout<<"Checked 0 records of '"<<zone.toString()<<"', 1 errors, 0 warnings."<<endl;
409 return 1;
410 }
411
412 NSEC3PARAMRecordContent ns3pr;
413 bool narrow = false;
414 bool haveNSEC3 = dk.getNSEC3PARAM(zone, &ns3pr, &narrow);
415 bool isOptOut=(haveNSEC3 && ns3pr.d_flags);
416
417 bool isSecure=dk.isSecuredZone(zone);
418 bool presigned=dk.isPresigned(zone);
419
420 DNSResourceRecord rr;
421 uint64_t numrecords=0, numerrors=0, numwarnings=0;
422
423
424 // Check for delegation in parent zone
425 string parent(zone);
426 while(chopOff(parent)) {
427 SOAData sd_p;
428 if(B.getSOAUncached(parent, sd_p)) {
429 bool ns=false;
430 DNSResourceRecord rr;
431 B.lookup(QType(QType::ANY), zone, NULL, sd_p.domain_id);
432 while(B.get(rr))
433 ns |= (rr.qtype == QType::NS);
434 if (!ns) {
435 cerr<<"[Error] No delegation for zone '"<<zone<<"' in parent '"<<parent<<"'"<<endl;
436 numerrors++;
437 }
438 break;
439 }
440 }
441
442
443 bool hasNsAtApex = false;
444 set<DNSName> cnames, noncnames, glue, checkglue;
445 set<string> records;
446 map<string, unsigned int> ttl;
447
448 ostringstream content;
449 pair<map<string, unsigned int>::iterator,bool> ret;
450
451 sd.db->list(zone, sd.domain_id, true);
452
453 while(sd.db->get(rr)) {
454 if(!rr.qtype.getCode())
455 continue;
456
457 numrecords++;
458
459 if(rr.qtype.getCode() == QType::SOA) {
460 vector<string>parts;
461 stringtok(parts, rr.content);
462
463 ostringstream o;
464 o<<rr.content;
465 for(int pleft=parts.size(); pleft < 7; ++pleft) {
466 o<<" 0";
467 }
468 rr.content=o.str();
469 }
470
471 if(rr.qtype.getCode() == QType::TXT && !rr.content.empty() && rr.content[0]!='"')
472 rr.content = "\""+rr.content+"\"";
473
474 try {
475 shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(rr.qtype.getCode(), 1, rr.content));
476 string tmp=drc->serialize(rr.qname);
477 tmp = drc->getZoneRepresentation();
478 if (rr.qtype.getCode() != QType::AAAA) {
479 if (!pdns_iequals(tmp, rr.content)) {
480 cout<<"[Warning] Parsed and original record content are not equal: "<<rr.qname.toString()<<" IN " <<rr.qtype.getName()<< " '" << rr.content<<"' (Content parsed as '"<<tmp<<"')"<<endl;
481 numwarnings++;
482 }
483 } else {
484 struct in6_addr tmpbuf;
485 if (inet_pton(AF_INET6, rr.content.c_str(), &tmpbuf) != 1 || rr.content.find('.') != string::npos) {
486 cout<<"[Warning] Following record is not a valid IPv6 address: "<<rr.qname.toString()<<" IN " <<rr.qtype.getName()<< " '" << rr.content<<"'"<<endl;
487 numwarnings++;
488 }
489 }
490 }
491 catch(std::exception& e)
492 {
493 cout<<"[Error] Following record had a problem: "<<rr.qname.toString()<<" IN " <<rr.qtype.getName()<< " " << rr.content<<endl;
494 cout<<"[Error] Error was: "<<e.what()<<endl;
495 numerrors++;
496 continue;
497 }
498
499 if(!rr.qname.isPartOf(zone)) {
500 cout<<"[Warning] Record '"<<rr.qname.toString()<<" IN "<<rr.qtype.getName()<<" "<<rr.content<<"' in zone '"<<zone.toString()<<"' is out-of-zone."<<endl;
501 numwarnings++;
502 continue;
503 }
504
505 content.str("");
506 content<<rr.qname.toString()<<" "<<rr.qtype.getName()<<" "<<rr.content;
507 if (records.count(toLower(content.str()))) {
508 cout<<"[Error] Duplicate record found in rrset: '"<<rr.qname.toString()<<" IN "<<rr.qtype.getName()<<" "<<rr.content<<"'"<<endl;
509 numerrors++;
510 continue;
511 } else
512 records.insert(toLower(content.str()));
513
514 content.str("");
515 content<<rr.qname.toString()<<" "<<rr.qtype.getName();
516 if (rr.qtype.getCode() == QType::RRSIG) {
517 RRSIGRecordContent rrc(rr.content);
518 content<<" ("<<DNSRecordContent::NumberToType(rrc.d_type)<<")";
519 }
520 ret = ttl.insert(pair<string, unsigned int>(toLower(content.str()), rr.ttl));
521 if (ret.second == false && ret.first->second != rr.ttl) {
522 cout<<"[Error] TTL mismatch in rrset: '"<<rr.qname.toString()<<" IN " <<rr.qtype.getName()<<" "<<rr.content<<"' ("<<ret.first->second<<" != "<<rr.ttl<<")"<<endl;
523 numerrors++;
524 continue;
525 }
526
527 if (isSecure && isOptOut && (rr.qname.countLabels() && rr.qname.getRawLabels()[0] == "*")) {
528 cout<<"[Warning] wildcard record '"<<rr.qname.toString()<<" IN " <<rr.qtype.getName()<<" "<<rr.content<<"' is insecure"<<endl;
529 cout<<"[Info] Wildcard records in opt-out zones are insecure. Disable the opt-out flag for this zone to avoid this warning. Command: pdnssec set-nsec3 "<<zone.toString()<<endl;
530 numwarnings++;
531 }
532
533 if(rr.qname==zone) {
534 if (rr.qtype.getCode() == QType::NS) {
535 hasNsAtApex=true;
536 } else if (rr.qtype.getCode() == QType::DS) {
537 cout<<"[Warning] DS at apex in zone '"<<zone.toString()<<"', should not be here."<<endl;
538 numwarnings++;
539 }
540 } else {
541 if (rr.qtype.getCode() == QType::SOA) {
542 cout<<"[Error] SOA record not at apex '"<<rr.qname.toString()<<" IN "<<rr.qtype.getName()<<" "<<rr.content<<"' in zone '"<<zone.toString()<<"'"<<endl;
543 numerrors++;
544 continue;
545 } else if (rr.qtype.getCode() == QType::DNSKEY) {
546 cout<<"[Warning] DNSKEY record not at apex '"<<rr.qname.toString()<<" IN "<<rr.qtype.getName()<<" "<<rr.content<<"' in zone '"<<zone.toString()<<"', should not be here."<<endl;
547 numwarnings++;
548 } else if (rr.qtype.getCode() == QType::NS && endsOn(rr.content, rr.qname)) {
549 checkglue.insert(toLower(rr.content));
550 } else if (rr.qtype.getCode() == QType::A || rr.qtype.getCode() == QType::AAAA) {
551 glue.insert(toLower(rr.qname));
552 }
553 }
554
555 if (rr.qtype.getCode() == QType::CNAME) {
556 if (!cnames.count(rr.qname))
557 cnames.insert(rr.qname);
558 else {
559 cout<<"[Error] Duplicate CNAME found at '"<<rr.qname.toString()<<"'"<<endl;
560 numerrors++;
561 continue;
562 }
563 } else {
564 if (rr.qtype.getCode() == QType::RRSIG) {
565 if(!presigned) {
566 cout<<"[Error] RRSIG found at '"<<rr.qname.toString()<<"' in non-presigned zone. These do not belong in the database."<<endl;
567 numerrors++;
568 continue;
569 }
570 } else
571 noncnames.insert(rr.qname);
572 }
573
574 if(rr.qtype.getCode() == QType::NSEC || rr.qtype.getCode() == QType::NSEC3)
575 {
576 cout<<"[Error] NSEC or NSEC3 found at '"<<rr.qname.toString()<<"'. These do not belong in the database."<<endl;
577 numerrors++;
578 continue;
579 }
580
581 if(!presigned && rr.qtype.getCode() == QType::DNSKEY)
582 {
583 if(::arg().mustDo("direct-dnskey"))
584 {
585 if(rr.ttl != sd.default_ttl)
586 {
587 cout<<"[Warning] DNSKEY TTL of "<<rr.ttl<<" at '"<<rr.qname.toString()<<"' differs from SOA minimum of "<<sd.default_ttl<<endl;
588 numwarnings++;
589 }
590 }
591 else
592 {
593 cout<<"[Warning] DNSKEY at '"<<rr.qname.toString()<<"' in non-presigned zone will mostly be ignored and can cause problems."<<endl;
594 numwarnings++;
595 }
596 }
597
598 // if (rr.qname[rr.qname.size()-1] == '.') {
599 // cout<<"[Error] Record '"<<rr.qname.toString()<<"' has a trailing dot. PowerDNS will ignore this record!"<<endl;
600 // numerrors++;
601 // }
602
603 if ( (rr.qtype.getCode() == QType::NS || rr.qtype.getCode() == QType::SRV || rr.qtype.getCode() == QType::MX || rr.qtype.getCode() == QType::CNAME) &&
604 rr.content[rr.content.size()-1] == '.') {
605 cout<<"[Warning] The record "<<rr.qname.toString()<<" with type "<<rr.qtype.getName()<<" has a trailing dot in the content ("<<rr.content<<"). Your backend might not work well with this."<<endl;
606 numwarnings++;
607 }
608
609 if(rr.auth == 0 && rr.qtype.getCode()!=QType::NS && rr.qtype.getCode()!=QType::A && rr.qtype.getCode()!=QType::AAAA)
610 {
611 cout<<"[Error] Following record is auth=0, run pdnssec rectify-zone?: "<<rr.qname.toString()<<" IN " <<rr.qtype.getName()<< " " << rr.content<<endl;
612 numerrors++;
613 }
614 }
615
616 for(auto &i: cnames) {
617 if (noncnames.find(i) != noncnames.end()) {
618 cout<<"[Error] CNAME "<<i.toString()<<" found, but other records with same label exist."<<endl;
619 numerrors++;
620 }
621 }
622
623 if(!hasNsAtApex) {
624 cout<<"[Error] No NS record at zone apex in zone '"<<zone.toString()<<"'"<<endl;
625 numerrors++;
626 }
627
628 for(const auto &qname : checkglue) {
629 if (!glue.count(qname)) {
630 cerr<<"[Warning] Missing glue for '"<<qname.toString()<<"' in zone '"<<zone.toString()<<"'"<<endl;
631 numwarnings++;
632 }
633 }
634
635 cout<<"Checked "<<numrecords<<" records of '"<<zone.toString()<<"', "<<numerrors<<" errors, "<<numwarnings<<" warnings."<<endl;
636 return numerrors;
637 }
638
639 int checkAllZones(DNSSECKeeper &dk)
640 {
641 UeberBackend B("default");
642 vector<DomainInfo> domainInfo;
643
644 B.getAllDomains(&domainInfo, true);
645 int errors=0;
646 BOOST_FOREACH(DomainInfo di, domainInfo) {
647 if (checkZone(dk, B, di.zone) > 0)
648 errors++;
649 }
650 cout<<"Checked "<<domainInfo.size()<<" zones, "<<errors<<" had errors."<<endl;
651 return 0;
652 }
653
654 int increaseSerial(const DNSName& zone, DNSSECKeeper &dk)
655 {
656 UeberBackend B("default");
657 SOAData sd;
658 if(!B.getSOAUncached(zone, sd)) {
659 cout<<"No SOA for zone '"<<zone.toString()<<"'"<<endl;
660 return -1;
661 }
662
663 string soaEditKind;
664 dk.getFromMeta(zone, "SOA-EDIT", soaEditKind);
665
666 sd.db->lookup(QType(QType::SOA), zone);
667 vector<DNSResourceRecord> rrs;
668 DNSResourceRecord rr;
669 while (sd.db->get(rr)) {
670 if (rr.qtype.getCode() == QType::SOA)
671 rrs.push_back(rr);
672 }
673
674 if (rrs.size() > 1) {
675 cerr<<rrs.size()<<" SOA records found for "<<zone.toString()<<"!"<<endl;
676 return -1;
677 }
678 if (rrs.size() < 1) {
679 cerr<<zone.toString()<<" not found!"<<endl;
680 }
681
682 if (soaEditKind.empty()) {
683 sd.serial++;
684 }
685 else if(pdns_iequals(soaEditKind,"INCREMENT-WEEKS")) {
686 sd.serial++;
687 }
688 else if(pdns_iequals(soaEditKind,"INCEPTION-INCREMENT")) {
689 uint32_t today_serial = localtime_format_YYYYMMDDSS(time(NULL), 1);
690
691 if (sd.serial < today_serial) {
692 sd.serial = today_serial;
693 }
694 else {
695 sd.serial++;
696 }
697 }
698 else {
699 sd.serial = calculateEditSOA(sd, soaEditKind) + 1;
700 }
701 rrs[0].content = serializeSOAData(sd);
702
703 sd.db->startTransaction("", -1);
704
705 if (! sd.db->replaceRRSet(sd.domain_id, zone, rr.qtype, rrs)) {
706 sd.db->abortTransaction();
707 cerr<<"Backend did not replace SOA record. Backend might not support this operation."<<endl;
708 return -1;
709 }
710
711 if (sd.db->doesDNSSEC()) {
712 NSEC3PARAMRecordContent ns3pr;
713 bool narrow;
714 bool haveNSEC3=dk.getNSEC3PARAM(zone, &ns3pr, &narrow);
715
716 if(haveNSEC3)
717 {
718 if(!narrow) {
719 string hashed=toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, rrs[0].qname));
720 if(g_verbose)
721 cerr<<"'"<<rrs[0].qname.toString()<<"' -> '"<< hashed <<"'"<<endl;
722 sd.db->updateDNSSECOrderAndAuthAbsolute(sd.domain_id, rrs[0].qname, hashed, 1);
723 }
724 else {
725 sd.db->nullifyDNSSECOrderNameAndUpdateAuth(sd.domain_id, rrs[0].qname, 1);
726 }
727 } else {
728 sd.db->updateDNSSECOrderAndAuth(sd.domain_id, zone, rrs[0].qname, 1);
729 }
730 }
731
732 sd.db->commitTransaction();
733
734 cout<<"SOA serial for zone "<<zone.toString()<<" set to "<<sd.serial<<endl;
735 return 0;
736 }
737
738 int deleteZone(const DNSName &zone) {
739 UeberBackend B;
740 DomainInfo di;
741 if (! B.getDomainInfo(zone, di)) {
742 cerr<<"Domain '"<<zone.toString()<<"' not found!"<<endl;
743 return 1;
744 }
745
746 if(di.backend->deleteDomain(zone))
747 return 0;
748
749 cerr<<"Failed to delete domain '"<<zone.toString()<<"'"<<endl;;
750 return 1;
751 }
752
753 int listZone(const DNSName &zone) {
754 UeberBackend B;
755 DomainInfo di;
756
757 if (! B.getDomainInfo(zone, di)) {
758 cerr<<"Domain '"<<zone.toString()<<"' not found!"<<endl;
759 return 1;
760 }
761 di.backend->list(zone, di.id);
762 DNSResourceRecord rr;
763 while(di.backend->get(rr)) {
764 if(rr.qtype.getCode()) {
765 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] != '.')
766 rr.content.append(1, '.');
767
768 cout<<rr.qname.toString()<<".\t"<<rr.ttl<<"\tIN\t"<<rr.qtype.getName()<<"\t"<<rr.content<<endl;
769 }
770 }
771 return 0;
772 }
773
774 int loadZone(DNSName zone, const string& fname) {
775 UeberBackend B;
776 DomainInfo di;
777
778 if (B.getDomainInfo(zone, di)) {
779 cerr<<"Domain '"<<zone.toString()<<"' exists already, replacing contents"<<endl;
780 }
781 else {
782 cerr<<"Creating '"<<zone.toString()<<"'"<<endl;
783 B.createDomain(zone);
784
785 if(!B.getDomainInfo(zone, di)) {
786 cerr<<"Domain '"<<zone.toString()<<"' was not created!"<<endl;
787 return 1;
788 }
789 }
790 DNSBackend* db = di.backend;
791 ZoneParserTNG zpt(fname, zone);
792
793 DNSResourceRecord rr;
794 if(!db->startTransaction(zone, di.id)) {
795 cerr<<"Unable to start transaction for load of zone '"<<zone.toString()<<"'"<<endl;
796 return 1;
797 }
798 rr.domain_id=di.id;
799 while(zpt.get(rr)) {
800 if(!rr.qname.isPartOf(zone) && rr.qname!=zone) {
801 cerr<<"File contains record named '"<<rr.qname.toString()<<"' which is not part of zone '"<<zone.toString()<<"'"<<endl;
802 return 1;
803 }
804 db->feedRecord(rr);
805 }
806 db->commitTransaction();
807 return 0;
808 }
809
810 int createZone(const DNSName &zone) {
811 UeberBackend B;
812 DomainInfo di;
813 if (B.getDomainInfo(zone, di)) {
814 cerr<<"Domain '"<<zone.toString()<<"' exists already"<<endl;
815 return 1;
816 }
817 cerr<<"Creating '"<<zone.toString()<<"'"<<endl;
818 B.createDomain(zone);
819
820 if(!B.getDomainInfo(zone, di)) {
821 cerr<<"Domain '"<<zone.toString()<<"' was not created!"<<endl;
822 return 1;
823 }
824 return 1;
825 }
826
827
828 int listAllZones(const string &type="") {
829
830 int kindFilter = -1;
831 if (type.size()) {
832 if (toUpper(type) == "MASTER")
833 kindFilter = 0;
834 else if (toUpper(type) == "SLAVE")
835 kindFilter = 1;
836 else if (toUpper(type) == "NATIVE")
837 kindFilter = 2;
838 else {
839 cerr<<"Syntax: pdnssec list-all-zones [master|slave|native]"<<endl;
840 return 1;
841 }
842 }
843
844 UeberBackend B("default");
845
846 vector<DomainInfo> domains;
847 B.getAllDomains(&domains);
848
849 int count = 0;
850 for (vector<DomainInfo>::const_iterator di=domains.begin(); di != domains.end(); di++) {
851 if (di->kind == kindFilter || kindFilter == -1) {
852 cout<<di->zone.toString()<<endl;
853 count++;
854 }
855 }
856
857 if (kindFilter != -1)
858 cout<<type<<" zonecount:"<<count<<endl;
859 else
860 cout<<"All zonecount:"<<count<<endl;
861 return 0;
862 }
863
864 void testAlgorithm(int algo)
865 {
866 DNSCryptoKeyEngine::testOne(algo);
867 }
868
869 void testAlgorithms()
870 {
871 DNSCryptoKeyEngine::testAll();
872 }
873
874 void testSpeed(DNSSECKeeper& dk, const string& zone, const string& remote, int cores)
875 {
876 DNSResourceRecord rr;
877 rr.qname="blah."+zone;
878 rr.qtype=QType::A;
879 rr.ttl=3600;
880 rr.auth=1;
881 rr.qclass = QClass::IN;
882 rr.d_place=DNSResourceRecord::ANSWER;
883
884 UeberBackend db("key-only");
885
886 if ( ! db.backends.size() )
887 {
888 throw runtime_error("No backends available for DNSSEC key storage");
889 }
890
891 ChunkedSigningPipe csp(zone, 1, remote, cores);
892
893 vector<DNSResourceRecord> signatures;
894 uint32_t rnd;
895 unsigned char* octets = (unsigned char*)&rnd;
896 char tmp[25];
897 DTime dt;
898 dt.set();
899 for(unsigned int n=0; n < 100000; ++n) {
900 rnd = random();
901 snprintf(tmp, sizeof(tmp), "%d.%d.%d.%d",
902 octets[0], octets[1], octets[2], octets[3]);
903 rr.content=tmp;
904
905 snprintf(tmp, sizeof(tmp), "r-%u", rnd);
906 rr.qname=string(tmp)+"."+zone;
907
908 if(csp.submit(rr))
909 while(signatures = csp.getChunk(), !signatures.empty())
910 ;
911 }
912 cerr<<"Flushing the pipe, "<<csp.d_signed<<" signed, "<<csp.d_queued<<" queued, "<<csp.d_outstanding<<" outstanding"<< endl;
913 cerr<<"Net speed: "<<csp.d_signed/ (dt.udiffNoReset()/1000000.0) << " sigs/s"<<endl;
914 while(signatures = csp.getChunk(true), !signatures.empty())
915 ;
916 cerr<<"Done, "<<csp.d_signed<<" signed, "<<csp.d_queued<<" queued, "<<csp.d_outstanding<<" outstanding"<< endl;
917 cerr<<"Net speed: "<<csp.d_signed/ (dt.udiff()/1000000.0) << " sigs/s"<<endl;
918 }
919
920 void verifyCrypto(const string& zone)
921 {
922 ZoneParserTNG zpt(zone);
923 DNSResourceRecord rr;
924 DNSKEYRecordContent drc;
925 RRSIGRecordContent rrc;
926 DSRecordContent dsrc;
927 vector<shared_ptr<DNSRecordContent> > toSign;
928 DNSName qname, apex;
929 dsrc.d_digesttype=0;
930 while(zpt.get(rr)) {
931 if(rr.qtype.getCode() == QType::DNSKEY) {
932 cerr<<"got DNSKEY!"<<endl;
933 apex=rr.qname;
934 drc = *dynamic_cast<DNSKEYRecordContent*>(DNSRecordContent::mastermake(QType::DNSKEY, 1, rr.content));
935 }
936 else if(rr.qtype.getCode() == QType::RRSIG) {
937 cerr<<"got RRSIG"<<endl;
938 rrc = *dynamic_cast<RRSIGRecordContent*>(DNSRecordContent::mastermake(QType::RRSIG, 1, rr.content));
939 }
940 else if(rr.qtype.getCode() == QType::DS) {
941 cerr<<"got DS"<<endl;
942 dsrc = *dynamic_cast<DSRecordContent*>(DNSRecordContent::mastermake(QType::DS, 1, rr.content));
943 }
944 else {
945 qname = rr.qname;
946 toSign.push_back(shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(rr.qtype.getCode(), 1, rr.content)));
947 }
948 }
949
950 string msg = getMessageForRRSET(qname, rrc, toSign);
951 cerr<<"Verify: "<<DNSCryptoKeyEngine::makeFromPublicKeyString(drc.d_algorithm, drc.d_key)->verify(msg, rrc.d_signature)<<endl;
952 if(dsrc.d_digesttype) {
953 cerr<<"Calculated DS: "<<apex.toString()<<" IN DS "<<makeDSFromDNSKey(apex, drc, dsrc.d_digesttype).getZoneRepresentation()<<endl;
954 cerr<<"Original DS: "<<apex.toString()<<" IN DS "<<dsrc.getZoneRepresentation()<<endl;
955 }
956 #if 0
957 DNSCryptoKeyEngine*key=DNSCryptoKeyEngine::makeFromISCString(drc, "Private-key-format: v1.2\n"
958 "Algorithm: 12 (ECC-GOST)\n"
959 "GostAsn1: MEUCAQAwHAYGKoUDAgITMBIGByqFAwICIwEGByqFAwICHgEEIgQg/9MiXtXKg9FDXDN/R9CmVhJDyuzRAIgh4tPwCu4NHIs=\n");
960 string resign=key->sign(hash);
961 cerr<<Base64Encode(resign)<<endl;
962 cerr<<"Verify: "<<DNSCryptoKeyEngine::makeFromPublicKeyString(drc.d_algorithm, drc.d_key)->verify(hash, resign)<<endl;
963 #endif
964
965 }
966 bool disableDNSSECOnZone(DNSSECKeeper& dk, const DNSName& zone)
967 {
968 UeberBackend B("default");
969 DomainInfo di;
970
971 if (!B.getDomainInfo(zone, di)){
972 cerr << "No such zone in the database" << endl;
973 return false;
974 }
975
976 if(!dk.isSecuredZone(zone)) {
977 cerr<<"Zone is not secured"<<endl;
978 return false;
979 }
980 DNSSECKeeper::keyset_t keyset=dk.getKeys(zone);
981
982 if(keyset.empty()) {
983 cerr << "No keys for zone '"<<zone.toString()<<"'."<<endl;
984 }
985 else {
986 BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, keyset) {
987 dk.deactivateKey(zone, value.second.id);
988 dk.removeKey(zone, value.second.id);
989 }
990 }
991 dk.unsetNSEC3PARAM(zone);
992 dk.unsetPresigned(zone);
993 return true;
994 }
995 bool showZone(DNSSECKeeper& dk, const DNSName& zone)
996 {
997 UeberBackend B("default");
998 DomainInfo di;
999 std::vector<std::string> meta;
1000
1001 if (!B.getDomainInfo(zone, di)){
1002 cerr << "No such zone in the database" << endl;
1003 return false;
1004 }
1005
1006 if(!dk.isSecuredZone(zone)) {
1007 cerr<<"Zone is not actively secured"<<endl;
1008 }
1009 NSEC3PARAMRecordContent ns3pr;
1010 bool narrow;
1011 bool haveNSEC3=dk.getNSEC3PARAM(zone, &ns3pr, &narrow);
1012
1013 DNSSECKeeper::keyset_t keyset=dk.getKeys(zone);
1014 if (B.getDomainMetadata(zone, "TSIG-ALLOW-AXFR", meta) && meta.size() > 0) {
1015 cerr << "Zone has following allowed TSIG key(s): " << boost::join(meta, ",") << endl;
1016 }
1017
1018 meta.clear();
1019 if (B.getDomainMetadata(zone, "AXFR-MASTER-TSIG", meta) && meta.size() > 0) {
1020 cerr << "Zone uses following TSIG key(s): " << boost::join(meta, ",") << endl;
1021 }
1022
1023 cout <<"Zone is " << (dk.isPresigned(zone) ? "" : "not ") << "presigned"<<endl;
1024
1025 if(keyset.empty()) {
1026 cerr << "No keys for zone '"<<zone.toString()<<"'."<<endl;
1027 }
1028 else {
1029 if(!haveNSEC3)
1030 cout<<"Zone has NSEC semantics"<<endl;
1031 else
1032 cout<<"Zone has " << (narrow ? "NARROW " : "") <<"hashed NSEC3 semantics, configuration: "<<ns3pr.getZoneRepresentation()<<endl;
1033
1034 cout << "keys: "<<endl;
1035 BOOST_FOREACH(DNSSECKeeper::keyset_t::value_type value, keyset) {
1036 string algname;
1037 algorithm2name(value.first.d_algorithm, algname);
1038 if (value.first.getKey()->getBits() < 1) {
1039 cout<<"ID = "<<value.second.id<<" ("<<(value.second.keyOrZone ? "KSK" : "ZSK")<<") <key missing or defunct>" <<endl;
1040 continue;
1041 }
1042 cout<<"ID = "<<value.second.id<<" ("<<(value.second.keyOrZone ? "KSK" : "ZSK")<<"), tag = "<<value.first.getDNSKEY().getTag();
1043 cout<<", algo = "<<(int)value.first.d_algorithm<<", bits = "<<value.first.getKey()->getBits()<<"\tActive: "<<value.second.active<< " ( " + algname + " ) "<<endl;
1044 if(value.second.keyOrZone || ::arg().mustDo("direct-dnskey") || g_verbose)
1045 cout<<(value.second.keyOrZone ? "KSK" : "ZSK")<<" DNSKEY = "<<zone.toString()<<" IN DNSKEY "<< value.first.getDNSKEY().getZoneRepresentation() << " ; ( " + algname + " )" << endl;
1046 if(value.second.keyOrZone || g_verbose) {
1047 cout<<"DS = "<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 1).getZoneRepresentation() << " ; ( SHA1 digest )" << endl;
1048 cout<<"DS = "<<zone.toString()<<" IN DS "<<makeDSFromDNSKey(zone, value.first.getDNSKEY(), 2).getZoneRepresentation() << " ; ( SHA256 digest )" << endl;
1049 try {
1050 string output=makeDSFromDNSKey(zone, value.first.getDNSKEY(), 3).getZoneRepresentation();
1051 cout<<"DS = "<<zone.toString()<<" IN DS "<< output << " ; ( GOST R 34.11-94 digest )" << endl;
1052 }
1053 catch(...)
1054 {
1055 }
1056 try {
1057 string output=makeDSFromDNSKey(zone, value.first.getDNSKEY(), 4).getZoneRepresentation();
1058 cout<<"DS = "<<zone.toString()<<" IN DS "<< output << " ; ( SHA-384 digest )" << endl;
1059 }
1060 catch(...)
1061 {
1062 }
1063 cout<<endl;
1064 }
1065 }
1066 }
1067 return true;
1068 }
1069
1070 bool secureZone(DNSSECKeeper& dk, const DNSName& zone)
1071 {
1072 // parse attribute
1073 vector<string> k_algos;
1074 vector<string> z_algos;
1075 int k_size;
1076 int z_size;
1077
1078 stringtok(k_algos, ::arg()["default-ksk-algorithms"], " ,");
1079 k_size = ::arg().asNum("default-ksk-size");
1080 stringtok(z_algos, ::arg()["default-zsk-algorithms"], " ,");
1081 z_size = ::arg().asNum("default-zsk-size");
1082
1083 if (k_size < 0) {
1084 throw runtime_error("KSK key size must be equal to or greater than 0");
1085 }
1086
1087 if (k_algos.size() < 1) {
1088 throw runtime_error("No algorithm(s) given for KSK");
1089 }
1090
1091 if (z_size < 0) {
1092 throw runtime_error("ZSK key size must be equal to or greater than 0");
1093 }
1094
1095 if (z_algos.size() < 1) {
1096 throw runtime_error("No algorithm(s) given for ZSK");
1097 }
1098
1099 if(dk.isSecuredZone(zone)) {
1100 cerr << "Zone '"<<zone.toString()<<"' already secure, remove keys with pdnssec remove-zone-key if needed"<<endl;
1101 return false;
1102 }
1103
1104 DomainInfo di;
1105 UeberBackend B("default");
1106 if(!B.getDomainInfo(zone, di) || !di.backend) { // di.backend and B are mostly identical
1107 cout<<"Can't find a zone called '"<<zone.toString()<<"'"<<endl;
1108 return false;
1109 }
1110
1111 if(di.kind == DomainInfo::Slave)
1112 {
1113 cout<<"Warning! This is a slave domain! If this was a mistake, please run"<<endl;
1114 cout<<"pdnssec disable-dnssec "<<zone.toString()<<" right now!"<<endl;
1115 }
1116
1117 if (k_size)
1118 cout << "Securing zone with " << k_algos[0] << " algorithm with key size " << k_size << endl;
1119 else
1120 cout << "Securing zone with " << k_algos[0] << " algorithm with default key size" << endl;
1121
1122 // run secure-zone with first default algorith, then add keys
1123 if(!dk.secureZone(zone, shorthand2algorithm(k_algos[0]), k_size)) {
1124 cerr<<"No backend was able to secure '"<<zone.toString()<<"', most likely because no DNSSEC"<<endl;
1125 cerr<<"capable backends are loaded, or because the backends have DNSSEC disabled."<<endl;
1126 cerr<<"For the Generic SQL backends, set the 'gsqlite3-dnssec', 'gmysql-dnssec' or"<<endl;
1127 cerr<<"'gpgsql-dnssec' flag. Also make sure the schema has been updated for DNSSEC!"<<endl;
1128 return false;
1129 }
1130
1131 if(!dk.isSecuredZone(zone)) {
1132 cerr<<"Failed to secure zone. Is your backend dnssec enabled? (set "<<endl;
1133 cerr<<"gsqlite3-dnssec, or gmysql-dnssec etc). Check this first."<<endl;
1134 cerr<<"If you run with the BIND backend, make sure you have configured"<<endl;
1135 cerr<<"it to use DNSSEC with 'bind-dnssec-db=/path/fname' and"<<endl;
1136 cerr<<"'pdnssec create-bind-db /path/fname'!"<<endl;
1137 return false;
1138 }
1139
1140 DNSSECKeeper::keyset_t zskset=dk.getKeys(zone, false);
1141
1142 if(!zskset.empty()) {
1143 cerr<<"There were ZSKs already for zone '"<<zone.toString()<<"', no need to add more"<<endl;
1144 return false;
1145 }
1146
1147 for(vector<string>::iterator i = k_algos.begin()+1; i != k_algos.end(); i++)
1148 dk.addKey(zone, true, shorthand2algorithm(*i), k_size, true); // obvious errors will have been caught above
1149
1150 BOOST_FOREACH(string z_algo, z_algos)
1151 {
1152 int algo = shorthand2algorithm(z_algo);
1153 dk.addKey(zone, false, algo, z_size);
1154 }
1155
1156 // rectifyZone(dk, zone);
1157 // showZone(dk, zone);
1158 cout<<"Zone "<<zone.toString()<<" secured"<<endl;
1159 return true;
1160 }
1161
1162 void testSchema(DNSSECKeeper& dk, const DNSName& zone)
1163 {
1164 cout<<"Note: test-schema will try to create the zone, but it will not remove it."<<endl;
1165 cout<<"Please clean up after this."<<endl;
1166 cout<<endl;
1167 cout<<"Constructing UeberBackend"<<endl;
1168 UeberBackend B("default");
1169 cout<<"Picking first backend - if this is not what you want, edit launch line!"<<endl;
1170 DNSBackend *db = B.backends[0];
1171 cout<<"Creating slave domain "<<zone.toString()<<endl;
1172 db->createSlaveDomain("127.0.0.1", zone, "", "_testschema");
1173 cout<<"Slave domain created"<<endl;
1174
1175 DomainInfo di;
1176 if(!B.getDomainInfo(zone, di) || !di.backend) { // di.backend and B are mostly identical
1177 cout<<"Can't find domain we just created, aborting"<<endl;
1178 return;
1179 }
1180 db=di.backend;
1181 DNSResourceRecord rr, rrget;
1182 cout<<"Starting transaction to feed records"<<endl;
1183 db->startTransaction(zone, di.id);
1184
1185 rr.qtype=QType::SOA;
1186 rr.qname=zone;
1187 rr.ttl=86400;
1188 rr.domain_id=di.id;
1189 rr.auth=1;
1190 rr.content="ns1.example.com. ahu.example.com. 2012081039 7200 3600 1209600 3600";
1191 cout<<"Feeding SOA"<<endl;
1192 db->feedRecord(rr);
1193 rr.qtype=QType::TXT;
1194 // 300 As
1195 rr.content="\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"";
1196 cout<<"Feeding overlong TXT"<<endl;
1197 db->feedRecord(rr);
1198 cout<<"Committing"<<endl;
1199 db->commitTransaction();
1200 cout<<"Querying TXT"<<endl;
1201 db->lookup(QType(QType::TXT), zone, NULL, di.id);
1202 if(db->get(rrget))
1203 {
1204 DNSResourceRecord rrthrowaway;
1205 if(db->get(rrthrowaway)) // should not touch rr but don't assume anything
1206 {
1207 cout<<"Expected one record, got multiple, aborting"<<endl;
1208 return;
1209 }
1210 int size=rrget.content.size();
1211 if(size != 302)
1212 {
1213 cout<<"Expected 302 bytes, got "<<size<<", aborting"<<endl;
1214 return;
1215 }
1216 }
1217 cout<<"[+] content field is over 255 bytes"<<endl;
1218
1219 cout<<"Dropping all records, inserting SOA+2xA"<<endl;
1220 db->startTransaction(zone, di.id);
1221
1222 rr.qtype=QType::SOA;
1223 rr.qname=zone;
1224 rr.ttl=86400;
1225 rr.domain_id=di.id;
1226 rr.auth=1;
1227 rr.content="ns1.example.com. ahu.example.com. 2012081039 7200 3600 1209600 3600";
1228 cout<<"Feeding SOA"<<endl;
1229 db->feedRecord(rr);
1230
1231 rr.qtype=QType::A;
1232 rr.qname="_underscore."+zone;
1233 rr.content="127.0.0.1";
1234 db->feedRecord(rr);
1235
1236 rr.qname="bla."+zone;
1237 cout<<"Committing"<<endl;
1238 db->commitTransaction();
1239
1240 cout<<"Securing zone"<<endl;
1241 secureZone(dk, zone);
1242 cout<<"Rectifying zone"<<endl;
1243 rectifyZone(dk, zone);
1244 cout<<"Checking underscore ordering"<<endl;
1245 DNSName before, after;
1246 db->getBeforeAndAfterNames(di.id, zone, "z."+zone, before, after);
1247 cout<<"got '"<<before.toString()<<"' < 'z."<<zone.toString()<<"' < '"<<after.toString()<<"'"<<endl;
1248 if(before != "_underscore."+zone)
1249 {
1250 cout<<"before is wrong, got '"<<before.toString()<<"', expected '_underscore."<<zone.toString()<<"', aborting"<<endl;
1251 return;
1252 }
1253 if(after != zone)
1254 {
1255 cout<<"after is wrong, got '"<<after.toString()<<"', expected '"<<zone.toString()<<"', aborting"<<endl;
1256 return;
1257 }
1258 cout<<"[+] ordername sorting is correct for names starting with _"<<endl;
1259 cout<<endl;
1260 cout<<"End of tests, please remove "<<zone.toString()<<" from domains+records"<<endl;
1261 }
1262
1263 int main(int argc, char** argv)
1264 try
1265 {
1266 po::options_description desc("Allowed options");
1267 desc.add_options()
1268 ("help,h", "produce help message")
1269 ("verbose,v", "be verbose")
1270 ("force", "force an action")
1271 ("config-name", po::value<string>()->default_value(""), "virtual configuration name")
1272 ("config-dir", po::value<string>()->default_value(SYSCONFDIR), "location of pdns.conf")
1273 ("commands", po::value<vector<string> >());
1274
1275 po::positional_options_description p;
1276 p.add("commands", -1);
1277 po::store(po::command_line_parser(argc, argv).options(desc).positional(p).run(), g_vm);
1278 po::notify(g_vm);
1279
1280 vector<string> cmds;
1281
1282 if(g_vm.count("commands"))
1283 cmds = g_vm["commands"].as<vector<string> >();
1284
1285 g_verbose = g_vm.count("verbose");
1286
1287 if(cmds.empty() || g_vm.count("help")) {
1288 cerr<<"Usage: \npdnssec [options] <command> [params ..]\n"<<endl;
1289 cerr<<"Commands:"<<endl;
1290 cerr<<"activate-tsig-key ZONE NAME [master|slave]"<<endl;
1291 cerr<<" Enable TSIG key for a zone"<<endl;
1292 cerr<<"activate-zone-key ZONE KEY-ID Activate the key with key id KEY-ID in ZONE"<<endl;
1293 cerr<<"add-zone-key ZONE zsk|ksk [bits] [active|passive]"<<endl;
1294 cerr<<" [rsasha1|rsasha256|rsasha512|gost|ecdsa256|ecdsa384]"<<endl;
1295 cerr<<" Add a ZSK or KSK to zone and specify algo&bits"<<endl;
1296 cerr<<"b2b-migrate old new Move all data from one backend to another"<<endl;
1297 cerr<<"bench-db [filename] Bench database backend with queries, one domain per line"<<endl;
1298 cerr<<"check-zone ZONE Check a zone for correctness"<<endl;
1299 cerr<<"check-all-zones Check all zones for correctness"<<endl;
1300 cerr<<"create-bind-db FNAME Create DNSSEC db for BIND backend (bind-dnssec-db)"<<endl;
1301 cerr<<"create-zone ZONE Create empty zone ZONE"<<endl;
1302 cerr<<"deactivate-tsig-key ZONE NAME [master|slave]"<<endl;
1303 cerr<<" Disable TSIG key for a zone"<<endl;
1304 cerr<<"deactivate-zone-key ZONE KEY-ID Deactivate the key with key id KEY-ID in ZONE"<<endl;
1305 cerr<<"delete-tsig-key NAME Delete TSIG key (warning! will not unmap key!)"<<endl;
1306 cerr<<"delete-zone ZONE Delete the zone"<<endl;
1307 cerr<<"disable-dnssec ZONE Deactivate all keys and unset PRESIGNED in ZONE"<<endl;
1308 cerr<<"export-zone-dnskey ZONE KEY-ID Export to stdout the public DNSKEY described"<<endl;
1309 cerr<<"export-zone-key ZONE KEY-ID Export to stdout the private key described"<<endl;
1310 cerr<<"generate-tsig-key NAME ALGORITHM Generate new TSIG key"<<endl;
1311 cerr<<"generate-zone-key zsk|ksk [algorithm] [bits]"<<endl;
1312 cerr<<" Generate a ZSK or KSK to stdout with specified algo&bits"<<endl;
1313 cerr<<"get-meta ZONE [kind kind ..] Get zone metadata. If no KIND given, lists all known"<<endl;
1314 cerr<<"hash-zone-record ZONE RNAME Calculate the NSEC3 hash for RNAME in ZONE"<<endl;
1315 #ifdef HAVE_P11KIT1
1316 cerr<<"hsm assign zone algorithm ksk|zsk module slot pin label"<<endl<<
1317 " Assign a hardware signing module to a ZONE"<<endl;
1318 cerr<<"hsm create-key zone key-id [bits] Create a key using hardware signing module for ZONE (use assign first)"<<endl;
1319 cerr<<" bits defaults to 2048"<<endl;
1320 #endif
1321 cerr<<"increase-serial ZONE Increases the SOA-serial by 1. Uses SOA-EDIT"<<endl;
1322 cerr<<"import-tsig-key NAME ALGORITHM KEY Import TSIG key"<<endl;
1323 cerr<<"import-zone-key ZONE FILE Import from a file a private key, ZSK or KSK"<<endl;
1324 cerr<<" [active|passive][ksk|zsk] Defaults to KSK and active"<<endl;
1325 cerr<<"load-zone ZONE FILE Load ZONE from FILE, possibly creating zone or atomically"<<endl;
1326 cerr<<" replacing contents"<<endl;
1327 cerr<<"list-zone ZONE List zone contents"<<endl;
1328 cerr<<"list-all-zones [master|slave|native]"<<endl;
1329 cerr<<" List all zone names"<<endl;;
1330 cerr<<"list-tsig-keys List all TSIG keys"<<endl;
1331 cerr<<"rectify-zone ZONE [ZONE ..] Fix up DNSSEC fields (order, auth)"<<endl;
1332 cerr<<"rectify-all-zones Rectify all zones."<<endl;
1333 cerr<<"remove-zone-key ZONE KEY-ID Remove key with KEY-ID from ZONE"<<endl;
1334 cerr<<"secure-all-zones [increase-serial] Secure all zones without keys."<<endl;
1335 cerr<<"secure-zone ZONE [ZONE ..] Add KSK and two ZSKs"<<endl;
1336 cerr<<"set-nsec3 ZONE ['params' [narrow]] Enable NSEC3 with PARAMs. Optionally narrow"<<endl;
1337 cerr<<"set-presigned ZONE Use presigned RRSIGs from storage"<<endl;
1338 cerr<<"set-meta ZONE KIND [value value ..]"<<endl;
1339 cerr<<" Set zone metadata, optionally providing a value. Empty clears meta."<<endl;
1340 cerr<<"show-zone ZONE Show DNSSEC (public) key details about a zone"<<endl;
1341 cerr<<"unset-nsec3 ZONE Switch back to NSEC"<<endl;
1342 cerr<<"unset-presigned ZONE No longer use presigned RRSIGs"<<endl;
1343 cerr<<"test-schema ZONE Test DB schema - will create ZONE"<<endl;
1344 cerr<<desc<<endl;
1345 return 0;
1346 }
1347
1348 if (cmds[0] == "test-algorithm") {
1349 if(cmds.size() != 2) {
1350 cerr << "Syntax: pdnssec test-algorithm algonum"<<endl;
1351 return 0;
1352 }
1353 testAlgorithm(lexical_cast<int>(cmds[1]));
1354 return 0;
1355 }
1356
1357 if(cmds[0] == "test-algorithms") {
1358 testAlgorithms();
1359 return 0;
1360 }
1361
1362 loadMainConfig(g_vm["config-dir"].as<string>());
1363 reportAllTypes();
1364
1365 if(cmds[0] == "create-bind-db") {
1366 #ifdef HAVE_SQLITE3
1367 if(cmds.size() != 2) {
1368 cerr << "Syntax: pdnssec create-bind-db fname"<<endl;
1369 return 0;
1370 }
1371 try {
1372 SSQLite3 db(cmds[1], true); // create=ok
1373 vector<string> statements;
1374 stringtok(statements, sqlCreate, ";");
1375 BOOST_FOREACH(const string& statement, statements) {
1376 db.execute(statement);
1377 }
1378 }
1379 catch(SSqlException& se) {
1380 throw PDNSException("Error creating database in BIND backend: "+se.txtReason());
1381 }
1382 return 0;
1383 #else
1384 cerr<<"bind-dnssec-db requires building PowerDNS with SQLite3"<<endl;
1385 return 1;
1386 #endif
1387 }
1388
1389 DNSSECKeeper dk;
1390
1391 if (cmds[0] == "test-schema") {
1392 if(cmds.size() != 2) {
1393 cerr << "Syntax: pdnssec test-schema ZONE"<<endl;
1394 return 0;
1395 }
1396 testSchema(dk, cmds[1]);
1397 return 0;
1398 }
1399 if(cmds[0] == "rectify-zone") {
1400 if(cmds.size() < 2) {
1401 cerr << "Syntax: pdnssec rectify-zone ZONE [ZONE..]"<<endl;
1402 return 0;
1403 }
1404 unsigned int exitCode = 0;
1405 for(unsigned int n = 1; n < cmds.size(); ++n)
1406 if (!rectifyZone(dk, cmds[n])) exitCode = 1;
1407 return exitCode;
1408 }
1409 else if (cmds[0] == "rectify-all-zones") {
1410 rectifyAllZones(dk);
1411 }
1412 else if(cmds[0] == "check-zone") {
1413 if(cmds.size() != 2) {
1414 cerr << "Syntax: pdnssec check-zone ZONE"<<endl;
1415 return 0;
1416 }
1417 UeberBackend B("default");
1418 exit(checkZone(dk, B, cmds[1]));
1419 }
1420 else if(cmds[0] == "bench-db") {
1421 dbBench(cmds.size() > 1 ? cmds[1] : "");
1422 }
1423 else if (cmds[0] == "check-all-zones") {
1424 exit(checkAllZones(dk));
1425 }
1426 else if (cmds[0] == "list-all-zones") {
1427 if (cmds.size() > 2) {
1428 cerr << "Syntax: pdnssec list-all-zones [master|slave|native]"<<endl;
1429 return 0;
1430 }
1431 if (cmds.size() == 2)
1432 return listAllZones(cmds[1]);
1433 return listAllZones();
1434 }
1435 else if (cmds[0] == "test-zone") {
1436 cerr << "Did you mean check-zone?"<<endl;
1437 return 0;
1438 }
1439 else if (cmds[0] == "test-all-zones") {
1440 cerr << "Did you mean check-all-zones?"<<endl;
1441 return 0;
1442 }
1443 #if 0
1444 else if(cmds[0] == "signing-server" )
1445 {
1446 signingServer();
1447 }
1448 else if(cmds[0] == "signing-slave")
1449 {
1450 launchSigningService(0);
1451 }
1452 #endif
1453 else if(cmds[0] == "test-speed") {
1454 if(cmds.size() < 2) {
1455 cerr << "Syntax: pdnssec test-speed numcores [signing-server]"<<endl;
1456 return 0;
1457 }
1458 testSpeed(dk, cmds[1], (cmds.size() > 3) ? cmds[3] : "", atoi(cmds[2].c_str()));
1459 }
1460 else if(cmds[0] == "verify-crypto") {
1461 if(cmds.size() != 2) {
1462 cerr << "Syntax: pdnssec verify-crypto FILE"<<endl;
1463 return 0;
1464 }
1465 verifyCrypto(cmds[1]);
1466 }
1467
1468 else if(cmds[0] == "show-zone") {
1469 if(cmds.size() != 2) {
1470 cerr << "Syntax: pdnssec show-zone ZONE"<<endl;
1471 return 0;
1472 }
1473 const string& zone=cmds[1];
1474 if (!showZone(dk, zone)) return 1;
1475 }
1476 else if(cmds[0] == "disable-dnssec") {
1477 if(cmds.size() != 2) {
1478 cerr << "Syntax: pdnssec disable-dnssec ZONE"<<endl;
1479 return 0;
1480 }
1481 const string& zone=cmds[1];
1482 if(!disableDNSSECOnZone(dk, zone)) {
1483 cerr << "Cannot disable DNSSEC on " << zone << endl;
1484 return 1;
1485 }
1486 }
1487 else if(cmds[0] == "activate-zone-key") {
1488 if(cmds.size() != 3) {
1489 cerr << "Syntax: pdnssec activate-zone-key ZONE KEY-ID"<<endl;
1490 return 0;
1491 }
1492 const string& zone=cmds[1];
1493 unsigned int id=atoi(cmds[2].c_str());
1494 if(!id)
1495 {
1496 cerr<<"Invalid KEY-ID"<<endl;
1497 return 1;
1498 }
1499 if (!dk.activateKey(zone, id)) {
1500 cerr<<"Activation of key failed"<<endl;
1501 return 1;
1502 }
1503 return 0;
1504 }
1505 else if(cmds[0] == "deactivate-zone-key") {
1506 if(cmds.size() != 3) {
1507 cerr << "Syntax: pdnssec deactivate-zone-key ZONE KEY-ID"<<endl;
1508 return 0;
1509 }
1510 const string& zone=cmds[1];
1511 unsigned int id=atoi(cmds[2].c_str());
1512 if(!id)
1513 {
1514 cerr<<"Invalid KEY-ID"<<endl;
1515 return 1;
1516 }
1517 if (!dk.deactivateKey(zone, id)) {
1518 cerr<<"Deactivation of key failed"<<endl;
1519 return 1;
1520 }
1521 return 0;
1522 }
1523 else if(cmds[0] == "add-zone-key") {
1524 if(cmds.size() < 3 ) {
1525 cerr << "Syntax: pdnssec add-zone-key ZONE zsk|ksk [bits] [rsasha1|rsasha256|rsasha512|gost|ecdsa256|ecdsa384]"<<endl;
1526 return 0;
1527 }
1528 const string& zone=cmds[1];
1529
1530 UeberBackend B("default");
1531 DomainInfo di;
1532
1533 if (!B.getDomainInfo(zone, di)){
1534 cerr << "No such zone in the database" << endl;
1535 return 0;
1536 }
1537
1538 // need to get algorithm, bits & ksk or zsk from commandline
1539 bool keyOrZone=false;
1540 int tmp_algo=0;
1541 int bits=0;
1542 int algorithm=8;
1543 bool active=false;
1544 for(unsigned int n=2; n < cmds.size(); ++n) {
1545 if(pdns_iequals(cmds[n], "zsk"))
1546 keyOrZone = false;
1547 else if(pdns_iequals(cmds[n], "ksk"))
1548 keyOrZone = true;
1549 else if((tmp_algo = shorthand2algorithm(cmds[n]))>0) {
1550 algorithm = tmp_algo;
1551 } else if(pdns_iequals(cmds[n], "active")) {
1552 active=true;
1553 } else if(pdns_iequals(cmds[n], "inactive") || pdns_iequals(cmds[n], "passive")) {
1554 active=false;
1555 } else if(atoi(cmds[n].c_str())) {
1556 bits = atoi(cmds[n].c_str());
1557 } else {
1558 cerr<<"Unknown algorithm, key flag or size '"<<cmds[n]<<"'"<<endl;
1559 exit(EXIT_FAILURE);;
1560 }
1561 }
1562 if(!dk.addKey(zone, keyOrZone, algorithm, bits, active)) {
1563 cerr<<"Adding key failed, perhaps DNSSEC not enabled in configuration?"<<endl;
1564 exit(1);
1565 }
1566 else {
1567 cerr<<"Added a " << (keyOrZone ? "KSK" : "ZSK")<<" with algorithm = "<<algorithm<<", active="<<active<<endl;
1568 if(bits)
1569 cerr<<"Requested specific key size of "<<bits<<" bits"<<endl;
1570 }
1571 }
1572 else if(cmds[0] == "remove-zone-key") {
1573 if(cmds.size() < 3) {
1574 cerr<<"Syntax: pdnssec remove-zone-key ZONE KEY-ID"<<endl;
1575 return 0;
1576 }
1577 const string& zone=cmds[1];
1578 unsigned int id=atoi(cmds[2].c_str());
1579 if (!dk.removeKey(zone, id)) {
1580 cerr<<"Cannot remove key " << id << " from " << zone <<endl;
1581 return 1;
1582 }
1583 return 0;
1584 }
1585 else if(cmds[0] == "delete-zone") {
1586 if(cmds.size() != 2) {
1587 cerr<<"Syntax: pdnssec delete-zone ZONE"<<endl;
1588 return 0;
1589 }
1590 exit(deleteZone(cmds[1]));
1591 }
1592 else if(cmds[0] == "create-zone") {
1593 if(cmds.size() != 2) {
1594 cerr<<"Syntax: pdnssec create-zone ZONE"<<endl;
1595 return 0;
1596 }
1597 exit(createZone(cmds[1]));
1598 }
1599 else if(cmds[0] == "list-zone") {
1600 if(cmds.size() != 2) {
1601 cerr<<"Syntax: pdnssec list-zone ZONE"<<endl;
1602 return 0;
1603 }
1604 if(cmds[1]==".")
1605 cmds[1].clear();
1606
1607 exit(listZone(cmds[1]));
1608 }
1609 else if(cmds[0] == "load-zone") {
1610 if(cmds.size() != 3) {
1611 cerr<<"Syntax: pdnssec load-zone ZONE FILENAME"<<endl;
1612 return 0;
1613 }
1614 if(cmds[1]==".")
1615 cmds[1].clear();
1616
1617 exit(loadZone(cmds[1], cmds[2]));
1618 }
1619 else if(cmds[0] == "secure-zone") {
1620 if(cmds.size() < 2) {
1621 cerr << "Syntax: pdnssec secure-zone ZONE"<<endl;
1622 return 0;
1623 }
1624 vector<string> mustRectify;
1625 dk.startTransaction();
1626 unsigned int zoneErrors=0;
1627 for(unsigned int n = 1; n < cmds.size(); ++n) {
1628 const string& zone=cmds[n];
1629 if(secureZone(dk, zone)) {
1630 mustRectify.push_back(zone);
1631 } else {
1632 zoneErrors++;
1633 }
1634 }
1635
1636 dk.commitTransaction();
1637 BOOST_FOREACH(string& zone, mustRectify)
1638 rectifyZone(dk, zone);
1639
1640 if (zoneErrors) {
1641 return 1;
1642 }
1643 return 0;
1644 }
1645 else if (cmds[0] == "secure-all-zones") {
1646 if (cmds.size() >= 2 && !pdns_iequals(cmds[1], "increase-serial")) {
1647 cerr << "Syntax: pdnssec secure-all-zones [increase-serial]"<<endl;
1648 return 0;
1649 }
1650
1651 UeberBackend B("default");
1652
1653 vector<DomainInfo> domainInfo;
1654 B.getAllDomains(&domainInfo);
1655
1656 unsigned int zonesSecured=0, zoneErrors=0;
1657 BOOST_FOREACH(DomainInfo di, domainInfo) {
1658 if(!dk.isSecuredZone(di.zone)) {
1659 cout<<"Securing "<<di.zone.toString()<<": ";
1660 if (secureZone(dk, di.zone)) {
1661 zonesSecured++;
1662 if (cmds.size() == 2) {
1663 if (!increaseSerial(di.zone, dk))
1664 continue;
1665 } else
1666 continue;
1667 }
1668 zoneErrors++;
1669 }
1670 }
1671
1672 cout<<"Secured: "<<zonesSecured<<" zones. Errors: "<<zoneErrors<<endl;
1673
1674 if (zoneErrors) {
1675 return 1;
1676 }
1677 return 0;
1678 }
1679 else if(cmds[0]=="set-nsec3") {
1680 if(cmds.size() < 2) {
1681 cerr<<"Syntax: pdnssec set-nsec3 ZONE 'params' [narrow]"<<endl;
1682 return 0;
1683 }
1684 string nsec3params = cmds.size() > 2 ? cmds[2] : "1 0 1 ab";
1685 bool narrow = cmds.size() > 3 && cmds[3]=="narrow";
1686 NSEC3PARAMRecordContent ns3pr(nsec3params);
1687
1688 DNSName zone(cmds[1]);
1689 if(!dk.isSecuredZone(zone)) {
1690 cerr<<"Zone '"<<zone.toString()<<"' is not secured, can't set NSEC3 parameters"<<endl;
1691 exit(EXIT_FAILURE);
1692 }
1693 dk.setNSEC3PARAM(zone, ns3pr, narrow);
1694
1695 if (!ns3pr.d_flags)
1696 cerr<<"NSEC3 set, please rectify-zone if your backend needs it"<<endl;
1697 else
1698 cerr<<"NSEC3 (opt-out) set, please rectify-zone if your backend needs it"<<endl;
1699 }
1700 else if(cmds[0]=="set-presigned") {
1701 if(cmds.size() < 2) {
1702 cerr<<"Syntax: pdnssec set-presigned ZONE"<<endl;
1703 return 0;
1704 }
1705 if (! dk.setPresigned(cmds[1])) {
1706 cerr << "Could not set presigned on for " << cmds[1] << endl;
1707 return 1;
1708 }
1709 return 0;
1710 }
1711 else if(cmds[0]=="unset-presigned") {
1712 if(cmds.size() < 2) {
1713 cerr<<"Syntax: pdnssec unset-presigned ZONE"<<endl;
1714 return 0;
1715 }
1716 if (! dk.unsetPresigned(cmds[1])) {
1717 cerr << "Could not unset presigned on for " << cmds[1] << endl;
1718 return 1;
1719 }
1720 return 0;
1721 }
1722 else if(cmds[0]=="hash-zone-record") {
1723 if(cmds.size() < 3) {
1724 cerr<<"Syntax: pdnssec hash-zone-record ZONE RNAME"<<endl;
1725 return 0;
1726 }
1727 DNSName zone(cmds[1]);
1728 string& record=cmds[2];
1729 NSEC3PARAMRecordContent ns3pr;
1730 bool narrow;
1731 if(!dk.getNSEC3PARAM(zone, &ns3pr, &narrow)) {
1732 cerr<<"The '"<<zone.toString()<<"' zone does not use NSEC3"<<endl;
1733 return 0;
1734 }
1735 if(narrow) {
1736 cerr<<"The '"<<zone.toString()<<"' zone uses narrow NSEC3, but calculating hash anyhow"<<endl;
1737 }
1738
1739 cout<<toBase32Hex(hashQNameWithSalt(ns3pr.d_iterations, ns3pr.d_salt, record))<<endl;
1740 }
1741 else if(cmds[0]=="unset-nsec3") {
1742 if(cmds.size() < 2) {
1743 cerr<<"Syntax: pdnssec unset-nsec3 ZONE"<<endl;
1744 return 0;
1745 }
1746 if ( ! dk.unsetNSEC3PARAM(cmds[1])) {
1747 cerr<<"Cannot unset NSEC3 param for " << cmds[1] << endl;
1748 return 1;
1749 }
1750 return 0;
1751 }
1752 else if(cmds[0]=="export-zone-key") {
1753 if(cmds.size() < 3) {
1754 cerr<<"Syntax: pdnssec export-zone-key ZONE KEY-ID"<<endl;
1755 return 0;
1756 }
1757
1758 string zone=cmds[1];
1759 unsigned int id=atoi(cmds[2].c_str());
1760 DNSSECPrivateKey dpk=dk.getKeyById(zone, id);
1761 cout << dpk.getKey()->convertToISC() <<endl;
1762 }
1763 else if(cmds[0]=="increase-serial") {
1764 if (cmds.size() < 2) {
1765 cerr<<"Syntax: pdnssec increase-serial ZONE"<<endl;
1766 return 0;
1767 }
1768 return increaseSerial(cmds[1], dk);
1769 }
1770 else if(cmds[0]=="import-zone-key-pem") {
1771 if(cmds.size() < 4) {
1772 cerr<<"Syntax: pdnssec import-zone-key-pem ZONE FILE algorithm [ksk|zsk]"<<endl;
1773 exit(1);
1774 }
1775 string zone=cmds[1];
1776 string fname=cmds[2];
1777 string line;
1778 ifstream ifs(fname.c_str());
1779 string tmp, interim, raw;
1780 while(getline(ifs, line)) {
1781 if(line[0]=='-')
1782 continue;
1783 trim(line);
1784 interim += line;
1785 }
1786 B64Decode(interim, raw);
1787 DNSSECPrivateKey dpk;
1788 DNSKEYRecordContent drc;
1789 shared_ptr<DNSCryptoKeyEngine> key(DNSCryptoKeyEngine::makeFromPEMString(drc, raw));
1790 dpk.setKey(key);
1791
1792 dpk.d_algorithm = atoi(cmds[3].c_str());
1793
1794 if(dpk.d_algorithm == 7)
1795 dpk.d_algorithm = 5;
1796
1797 cerr<<(int)dpk.d_algorithm<<endl;
1798
1799 if(cmds.size() > 4) {
1800 if(pdns_iequals(cmds[4], "ZSK"))
1801 dpk.d_flags = 256;
1802 else if(pdns_iequals(cmds[4], "KSK"))
1803 dpk.d_flags = 257;
1804 else {
1805 cerr<<"Unknown key flag '"<<cmds[4]<<"'"<<endl;
1806 exit(1);
1807 }
1808 }
1809 else
1810 dpk.d_flags = 257; // ksk
1811
1812 if(!dk.addKey(zone, dpk)) {
1813 cerr<<"Adding key failed, perhaps DNSSEC not enabled in configuration?"<<endl;
1814 exit(1);
1815 }
1816
1817 }
1818 else if(cmds[0]=="import-zone-key") {
1819 if(cmds.size() < 3) {
1820 cerr<<"Syntax: pdnssec import-zone-key ZONE FILE [ksk|zsk] [active|passive]"<<endl;
1821 exit(1);
1822 }
1823 string zone=cmds[1];
1824 string fname=cmds[2];
1825 DNSSECPrivateKey dpk;
1826 DNSKEYRecordContent drc;
1827 shared_ptr<DNSCryptoKeyEngine> key(DNSCryptoKeyEngine::makeFromISCFile(drc, fname.c_str()));
1828 dpk.setKey(key);
1829 dpk.d_algorithm = drc.d_algorithm;
1830
1831 if(dpk.d_algorithm == 7)
1832 dpk.d_algorithm = 5;
1833
1834 dpk.d_flags = 257;
1835 bool active=true;
1836
1837 for(unsigned int n = 3; n < cmds.size(); ++n) {
1838 if(pdns_iequals(cmds[n], "ZSK"))
1839 dpk.d_flags = 256;
1840 else if(pdns_iequals(cmds[n], "KSK"))
1841 dpk.d_flags = 257;
1842 else if(pdns_iequals(cmds[n], "active"))
1843 active = 1;
1844 else if(pdns_iequals(cmds[n], "passive") || pdns_iequals(cmds[n], "inactive"))
1845 active = 0;
1846 else {
1847 cerr<<"Unknown key flag '"<<cmds[n]<<"'"<<endl;
1848 exit(1);
1849 }
1850 }
1851 if(!dk.addKey(zone, dpk, active)) {
1852 cerr<<"Adding key failed, perhaps DNSSEC not enabled in configuration?"<<endl;
1853 exit(1);
1854 }
1855 }
1856 else if(cmds[0]=="export-zone-dnskey") {
1857 if(cmds.size() < 3) {
1858 cerr<<"Syntax: pdnssec export-zone-dnskey ZONE KEY-ID"<<endl;
1859 exit(1);
1860 }
1861
1862 string zone=cmds[1];
1863 unsigned int id=atoi(cmds[2].c_str());
1864 DNSSECPrivateKey dpk=dk.getKeyById(zone, id);
1865 cout << zone<<" IN DNSKEY "<<dpk.getDNSKEY().getZoneRepresentation() <<endl;
1866 if(dpk.d_flags == 257) {
1867 cout << zone << " IN DS "<<makeDSFromDNSKey(zone, dpk.getDNSKEY(), 1).getZoneRepresentation() << endl;
1868 cout << zone << " IN DS "<<makeDSFromDNSKey(zone, dpk.getDNSKEY(), 2).getZoneRepresentation() << endl;
1869 }
1870 }
1871 else if(cmds[0] == "generate-zone-key") {
1872 if(cmds.size() < 2 ) {
1873 cerr << "Syntax: pdnssec generate-zone-key zsk|ksk [rsasha1|rsasha256|rsasha512|gost|ecdsa256|ecdsa384] [bits]"<<endl;
1874 return 0;
1875 }
1876 // need to get algorithm, bits & ksk or zsk from commandline
1877 bool keyOrZone=false;
1878 int tmp_algo=0;
1879 int bits=0;
1880 int algorithm=8;
1881 for(unsigned int n=1; n < cmds.size(); ++n) {
1882 if(pdns_iequals(cmds[n], "zsk"))
1883 keyOrZone = false;
1884 else if(pdns_iequals(cmds[n], "ksk"))
1885 keyOrZone = true;
1886 else if((tmp_algo = shorthand2algorithm(cmds[n]))>0) {
1887 algorithm = tmp_algo;
1888 } else if(atoi(cmds[n].c_str()))
1889 bits = atoi(cmds[n].c_str());
1890 else {
1891 cerr<<"Unknown algorithm, key flag or size '"<<cmds[n]<<"'"<<endl;
1892 return 0;
1893 }
1894 }
1895 cerr<<"Generating a " << (keyOrZone ? "KSK" : "ZSK")<<" with algorithm = "<<algorithm<<endl;
1896 if(bits)
1897 cerr<<"Requesting specific key size of "<<bits<<" bits"<<endl;
1898
1899 DNSSECPrivateKey dspk;
1900 shared_ptr<DNSCryptoKeyEngine> dpk(DNSCryptoKeyEngine::make(algorithm)); // defaults to RSA for now, could be smart w/algorithm! XXX FIXME
1901 if(!bits) {
1902 if(algorithm <= 10)
1903 bits = keyOrZone ? 2048 : 1024;
1904 else {
1905 if(algorithm == 12 || algorithm == 13 || algorithm == 250) // ECDSA, GOST, ED25519
1906 bits = 256;
1907 else if(algorithm == 14)
1908 bits = 384;
1909 else {
1910 throw runtime_error("Can't guess key size for algorithm "+lexical_cast<string>(algorithm));
1911 }
1912 }
1913 }
1914 dpk->create(bits);
1915 dspk.setKey(dpk);
1916 dspk.d_algorithm = algorithm;
1917 dspk.d_flags = keyOrZone ? 257 : 256;
1918
1919 // print key to stdout
1920 cout << "Flags: " << dspk.d_flags << endl <<
1921 dspk.getKey()->convertToISC() << endl;
1922 } else if (cmds[0]=="generate-tsig-key") {
1923 if (cmds.size() < 3) {
1924 cerr << "Syntax: " << cmds[0] << " name (hmac-md5|hmac-sha1|hmac-sha224|hmac-sha256|hmac-sha384|hmac-sha512)" << endl;
1925 return 0;
1926 }
1927 string name = cmds[1];
1928 string algo = cmds[2];
1929 string key;
1930 char tmpkey[64];
1931
1932 size_t klen = 0;
1933 if (algo == "hmac-md5") {
1934 klen = 32;
1935 } else if (algo == "hmac-sha1") {
1936 klen = 32;
1937 } else if (algo == "hmac-sha224") {
1938 klen = 32;
1939 } else if (algo == "hmac-sha256") {
1940 klen = 64;
1941 } else if (algo == "hmac-sha384") {
1942 klen = 64;
1943 } else if (algo == "hmac-sha512") {
1944 klen = 64;
1945 } else {
1946 cerr << "Cannot generate key for " << algo << endl;
1947 return 1;
1948 }
1949
1950 cerr << "Generating new key with " << klen << " bytes (this can take a while)" << endl;
1951 seedRandom(::arg()["entropy-source"]);
1952 for(size_t i = 0; i < klen; i+=4) {
1953 *(unsigned int*)(tmpkey+i) = dns_random(0xffffffff);
1954 }
1955 key = Base64Encode(std::string(tmpkey, klen));
1956
1957 UeberBackend B("default");
1958 if (B.setTSIGKey(name, algo, key)) {
1959 cout << "Create new TSIG key " << name << " " << algo << " " << key << endl;
1960 } else {
1961 cout << "Failure storing new TSIG key " << name << " " << algo << " " << key << endl;
1962 return 1;
1963 }
1964 return 0;
1965 } else if (cmds[0]=="import-tsig-key") {
1966 if (cmds.size() < 4) {
1967 cerr << "Syntax: " << cmds[0] << " name algorithm key" << endl;
1968 return 0;
1969 }
1970 string name = cmds[1];
1971 string algo = cmds[2];
1972 string key = cmds[3];
1973
1974 UeberBackend B("default");
1975 if (B.setTSIGKey(name, algo, key)) {
1976 cout << "Imported TSIG key " << name << " " << algo << endl;
1977 } else {
1978 cout << "Failure importing TSIG key " << name << " " << algo << endl;
1979 return 1;
1980 }
1981 return 0;
1982 } else if (cmds[0]=="delete-tsig-key") {
1983 if (cmds.size() < 2) {
1984 cerr << "Syntax: " << cmds[0] << " name" << endl;
1985 return 0;
1986 }
1987 string name = cmds[1];
1988
1989 UeberBackend B("default");
1990 if (B.deleteTSIGKey(name)) {
1991 cout << "Deleted TSIG key " << name << endl;
1992 } else {
1993 cout << "Failure deleting TSIG key " << name << endl;
1994 return 1;
1995 }
1996 return 0;
1997 } else if (cmds[0]=="list-tsig-keys") {
1998 std::vector<struct TSIGKey> keys;
1999 UeberBackend B("default");
2000 if (B.getTSIGKeys(keys)) {
2001 BOOST_FOREACH(const struct TSIGKey &key, keys) {
2002 cout << key.name.toString() << " " << key.algorithm.toString() << " " << key.key << endl;
2003 }
2004 }
2005 return 0;
2006 } else if (cmds[0]=="activate-tsig-key") {
2007 string metaKey;
2008 if (cmds.size() < 4) {
2009 cerr << "Syntax: " << cmds[0] << " zone name [master|slave]" << endl;
2010 return 0;
2011 }
2012 string zname = cmds[1];
2013 string name = cmds[2];
2014 if (cmds[3] == "master")
2015 metaKey = "TSIG-ALLOW-AXFR";
2016 else if (cmds[3] == "slave")
2017 metaKey = "AXFR-MASTER-TSIG";
2018 else {
2019 cerr << "Invalid parameter '" << cmds[3] << "', expected master or slave" << endl;
2020 return 1;
2021 }
2022 UeberBackend B("default");
2023 std::vector<std::string> meta;
2024 if (!B.getDomainMetadata(zname, metaKey, meta)) {
2025 cout << "Failure enabling TSIG key " << name << " for " << zname << endl;
2026 return 1;
2027 }
2028 bool found = false;
2029 BOOST_FOREACH(std::string tmpname, meta) {
2030 if (tmpname == name) { found = true; break; }
2031 }
2032 if (!found) meta.push_back(name);
2033 if (B.setDomainMetadata(zname, metaKey, meta)) {
2034 cout << "Enabled TSIG key " << name << " for " << zname << endl;
2035 } else {
2036 cout << "Failure enabling TSIG key " << name << " for " << zname << endl;
2037 return 1;
2038 }
2039 return 0;
2040 } else if (cmds[0]=="deactivate-tsig-key") {
2041 string metaKey;
2042 if (cmds.size() < 4) {
2043 cerr << "Syntax: " << cmds[0] << " zone name [master|slave]" << endl;
2044 return 0;
2045 }
2046 string zname = cmds[1];
2047 string name = cmds[2];
2048 if (cmds[3] == "master")
2049 metaKey = "TSIG-ALLOW-AXFR";
2050 else if (cmds[3] == "slave")
2051 metaKey = "AXFR-MASTER-TSIG";
2052 else {
2053 cerr << "Invalid parameter '" << cmds[3] << "', expected master or slave" << endl;
2054 return 1;
2055 }
2056
2057 UeberBackend B("default");
2058 std::vector<std::string> meta;
2059 if (!B.getDomainMetadata(zname, metaKey, meta)) {
2060 cout << "Failure disabling TSIG key " << name << " for " << zname << endl;
2061 return 1;
2062 }
2063 std::vector<std::string>::iterator iter = meta.begin();
2064 for(;iter != meta.end(); iter++) if (*iter == name) break;
2065 if (iter != meta.end()) meta.erase(iter);
2066 if (B.setDomainMetadata(zname, metaKey, meta)) {
2067 cout << "Disabled TSIG key " << name << " for " << zname << endl;
2068 } else {
2069 cout << "Failure disabling TSIG key " << name << " for " << zname << endl;
2070 return 1;
2071 }
2072 return 0;
2073 } else if (cmds[0]=="get-meta") {
2074 UeberBackend B("default");
2075 if (cmds.size() < 2) {
2076 cerr << "Syntax: " << cmds[0] << " zone [kind kind ..]" << endl;
2077 return 1;
2078 }
2079 string zone = cmds[1];
2080 vector<string> keys;
2081 DomainInfo di;
2082
2083 if (!B.getDomainInfo(zone, di)) {
2084 cerr << "Invalid zone '" << zone << "'" << endl;
2085 return 1;
2086 }
2087
2088 if (cmds.size() > 2) {
2089 keys.assign(cmds.begin() + 2, cmds.end());
2090 std::cout << "Metadata for '" << zone << "'" << endl;
2091 BOOST_FOREACH(const string kind, keys) {
2092 vector<string> meta;
2093 meta.clear();
2094 if (B.getDomainMetadata(zone, kind, meta)) {
2095 cout << kind << " = " << boost::join(meta, ", ") << endl;
2096 }
2097 }
2098 } else {
2099 std::map<std::string, std::vector<std::string> > meta;
2100 std::cout << "Metadata for '" << zone << "'" << endl;
2101 B.getAllDomainMetadata(zone, meta);
2102 for(std::map<std::string, std::vector<std::string> >::const_iterator each_meta = meta.begin(); each_meta != meta.end(); each_meta++) {
2103 cout << each_meta->first << " = " << boost::join(each_meta->second, ", ") << endl;
2104 }
2105 }
2106 return 0;
2107
2108 } else if (cmds[0]=="set-meta") {
2109 UeberBackend B("default");
2110 if (cmds.size() < 3) {
2111 cerr << "Syntax: " << cmds[0] << " zone kind [value value ..]" << endl;
2112 return 1;
2113 }
2114 string zone = cmds[1];
2115 string kind = cmds[2];
2116 vector<string> meta(cmds.begin() + 3, cmds.end());
2117
2118 if (!B.setDomainMetadata(zone, kind, meta)) {
2119 cerr << "Unable to set meta for '" << zone << "'" << endl;
2120 return 1;
2121 } else {
2122 cout << "Set '" << zone << "' meta " << kind << " = " << boost::join(meta, ", ") << endl;
2123 }
2124 } else if (cmds[0]=="hsm") {
2125 #ifdef HAVE_P11KIT1
2126 UeberBackend B("default");
2127 if (cmds[1] == "assign") {
2128 DNSCryptoKeyEngine::storvector_t storvect;
2129 DomainInfo di;
2130 std::vector<DNSBackend::KeyData> keys;
2131
2132 if (cmds.size() < 9) {
2133 std::cout << "Usage: pdnssec hsm assign zone algorithm ksk|zsk module slot pin label" << std::endl;
2134 return 1;
2135 }
2136
2137 string zone = cmds[2];
2138
2139 // verify zone
2140 if (!B.getDomainInfo(zone, di)) {
2141 cerr << "Unable to assign module to unknown zone '" << zone << "'" << std::endl;
2142 return 1;
2143 }
2144
2145 int algorithm = shorthand2algorithm(cmds[3]);
2146 if (algorithm<0) {
2147 cerr << "Unable to use unknown algorithm '" << cmds[3] << "'" << std::endl;
2148 return 1;
2149 }
2150
2151 int id;
2152 bool keyOrZone = (cmds[4] == "ksk" ? true : false);
2153 string module = cmds[5];
2154 string slot = cmds[6];
2155 string pin = cmds[7];
2156 string label = cmds[8];
2157
2158 std::ostringstream iscString;
2159 iscString << "Private-key-format: v1.2" << std::endl <<
2160 "Algorithm: " << algorithm << std::endl <<
2161 "Engine: " << module << std::endl <<
2162 "Slot: " << slot << std::endl <<
2163 "PIN: " << pin << std::endl <<
2164 "Label: " << label << std::endl;
2165
2166 DNSKEYRecordContent drc;
2167 DNSSECPrivateKey dpk;
2168 dpk.d_flags = (keyOrZone ? 257 : 256);
2169 dpk.setKey(shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromISCString(drc, iscString.str())));
2170
2171 // make sure this key isn't being reused.
2172 B.getDomainKeys(zone, 0, keys);
2173 id = -1;
2174
2175 BOOST_FOREACH(DNSBackend::KeyData& kd, keys) {
2176 if (kd.content == iscString.str()) {
2177 // it's this one, I guess...
2178 id = kd.id;
2179 break;
2180 }
2181 }
2182
2183 if (id > -1) {
2184 cerr << "You have already assigned this key with ID=" << id << std::endl;
2185 return 1;
2186 }
2187
2188 if (!(id = dk.addKey(zone, dpk))) {
2189 cerr << "Unable to assign module slot to zone" << std::endl;
2190 return 1;
2191 }
2192
2193 // figure out key id.
2194
2195 B.getDomainKeys(zone, 0, keys);
2196
2197 // validate which one got the key...
2198 BOOST_FOREACH(DNSBackend::KeyData& kd, keys) {
2199 if (kd.content == iscString.str()) {
2200 // it's this one, I guess...
2201 id = kd.id;
2202 break;
2203 }
2204 }
2205
2206 cerr << "Module " << module << " slot " << slot << " assigned to " << zone << " with key id " << id << endl;
2207
2208 return 0;
2209 } else if (cmds[1] == "create-key") {
2210
2211 if (cmds.size() < 4) {
2212 cerr << "Usage: pdnssec hsm create-key zone key-id [bits]" << endl;
2213 return 1;
2214 }
2215 DomainInfo di;
2216 string zone = cmds[2];
2217 unsigned int id;
2218 int bits = 2048;
2219 // verify zone
2220 if (!B.getDomainInfo(zone, di)) {
2221 cerr << "Unable to create key for unknown zone '" << zone << "'" << std::endl;
2222 return 1;
2223 }
2224
2225 id = boost::lexical_cast<unsigned int>(cmds[3]);
2226 std::vector<DNSBackend::KeyData> keys;
2227 if (!B.getDomainKeys(zone, 0, keys)) {
2228 cerr << "No keys found for zone " << zone << std::endl;
2229 return 1;
2230 }
2231
2232 DNSCryptoKeyEngine *dke = NULL;
2233 // lookup correct key
2234 BOOST_FOREACH(DNSBackend::KeyData &kd, keys) {
2235 if (kd.id == id) {
2236 // found our key.
2237 DNSKEYRecordContent dkrc;
2238 dke = DNSCryptoKeyEngine::makeFromISCString(dkrc, kd.content);
2239 }
2240 }
2241
2242 if (!dke) {
2243 cerr << "Could not find key with ID " << id << endl;
2244 return 1;
2245 }
2246 if (cmds.size() > 4) {
2247 bits = boost::lexical_cast<int>(cmds[4]);
2248 }
2249 if (bits < 1) {
2250 cerr << "Invalid bit size " << bits << "given, must be positive integer";
2251 return 1;
2252 }
2253 try {
2254 dke->create(bits);
2255 } catch (PDNSException& e) {
2256 cerr << e.reason << endl;
2257 return 1;
2258 }
2259
2260 cerr << "Key of size " << bits << " created" << std::endl;
2261 return 0;
2262 }
2263 #else
2264 cerr<<"PKCS#11 support not enabled"<<endl;
2265 return 1;
2266 #endif
2267 } else if (cmds[0] == "b2b-migrate") {
2268 if (cmds.size() < 3) {
2269 cerr<<"Usage: b2b-migrate old new"<<endl;
2270 return 1;
2271 }
2272
2273 DNSBackend *src,*tgt;
2274 src = tgt = NULL;
2275
2276 for(DNSBackend *b : BackendMakers().all()) {
2277 if (b->getPrefix() == cmds[1]) src = b;
2278 if (b->getPrefix() == cmds[2]) tgt = b;
2279 }
2280 if (!src) {
2281 cerr<<"Unknown source backend '"<<cmds[1]<<"'"<<endl;
2282 return 1;
2283 }
2284 if (!tgt) {
2285 cerr<<"Unknown target backend '"<<cmds[2]<<"'"<<endl;
2286 return 1;
2287 }
2288
2289 cout<<"Moving zone(s) from "<<src->getPrefix()<<" to "<<tgt->getPrefix()<<endl;
2290
2291 vector<DomainInfo> domains;
2292
2293 tgt->getAllDomains(&domains, true);
2294 if (domains.size()>0)
2295 throw PDNSException("Target backend has domain(s), please clean it first");
2296
2297 src->getAllDomains(&domains, true);
2298 // iterate zones
2299 for(const DomainInfo& di: domains) {
2300 size_t nr,nc,nm,nk;
2301 DNSResourceRecord rr;
2302 cout<<"Processing '"<<di.zone<<"'"<<endl;
2303 // create zone
2304 if (!tgt->createDomain(di.zone)) throw PDNSException("Failed to create zone");
2305 tgt->setKind(di.zone, di.kind);
2306 tgt->setAccount(di.zone,di.account);
2307 for(const string& master: di.masters) {
2308 tgt->setMaster(di.zone, master);
2309 }
2310 // move records
2311 if (!src->list(di.zone, di.id, true)) throw PDNSException("Failed to list records");
2312 nr=0;
2313 while(src->get(rr)) {
2314 if (!tgt->feedRecord(rr)) throw PDNSException("Failed to feed record");
2315 nr++;
2316 }
2317 // move comments
2318 nc=0;
2319 if (src->listComments(di.id)) {
2320 Comment c;
2321 while(src->getComment(c)) {
2322 tgt->feedComment(c);
2323 nc++;
2324 }
2325 }
2326 // move metadata
2327 nm=0;
2328 std::map<std::string, std::vector<std::string> > meta;
2329 if (src->getAllDomainMetadata(di.zone, meta)) {
2330 std::map<std::string, std::vector<std::string> >::iterator i;
2331 for(i=meta.begin(); i != meta.end(); i++) {
2332 if (!tgt->setDomainMetadata(di.zone, i->first, i->second)) throw PDNSException("Failed to feed domain metadata");
2333 nm++;
2334 }
2335 }
2336 // move keys
2337 nk=0;
2338 std::vector<DNSBackend::KeyData> keys;
2339 if (src->getDomainKeys(di.zone, 0, keys)) {
2340 for(const DNSBackend::KeyData& k: keys) {
2341 tgt->addDomainKey(di.zone, k);
2342 nk++;
2343 }
2344 }
2345 cout<<"Moved "<<nr<<" record(s), "<<nc<<" comment(s), "<<nm<<" metadata(s) and "<<nk<<" cryptokey(s)"<<endl;
2346 }
2347
2348 int ntk=0;
2349 // move tsig keys
2350 std::vector<struct TSIGKey> tkeys;
2351 if (src->getTSIGKeys(tkeys)) {
2352 for(const struct TSIGKey& tk: tkeys) {
2353 if (!tgt->setTSIGKey(tk.name, tk.algorithm, tk.key)) throw PDNSException("Failed to feed TSIG key");
2354 ntk++;
2355 }
2356 }
2357 cout<<"Moved "<<ntk<<" TSIG key(s)"<<endl;
2358
2359 cout<<"Remember to drop the old backend and run rectify-all-zones"<<endl;
2360
2361 return 0;
2362 } else {
2363 cerr<<"Unknown command '"<<cmds[0] <<"'"<< endl;
2364 return 1;
2365 }
2366 return 0;
2367 }
2368 catch(PDNSException& ae) {
2369 cerr<<"Error: "<<ae.reason<<endl;
2370 return 1;
2371 }
2372 catch(std::exception& e) {
2373 cerr<<"Error: "<<e.what()<<endl;
2374 return 1;
2375 }
2376 catch(...)
2377 {
2378 cerr<<"Caught an unknown exception"<<endl;
2379 return 1;
2380 }