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