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