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