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