2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include "bindbackend2.hh"
27 #include "pdns/arguments.hh"
28 #include "pdns/dnsrecords.hh"
32 void Bind2Backend::setupDNSSEC()
34 if(!getArg("dnssec-db").empty())
35 throw runtime_error("bind-dnssec-db requires building PowerDNS with SQLite3");
38 bool Bind2Backend::doesDNSSEC()
41 bool Bind2Backend::getNSEC3PARAM(const DNSName
& name
, NSEC3PARAMRecordContent
* ns3p
)
44 bool Bind2Backend::getAllDomainMetadata(const DNSName
& name
, std::map
<std::string
, std::vector
<std::string
> >& meta
)
47 bool Bind2Backend::getDomainMetadata(const DNSName
& name
, const std::string
& kind
, std::vector
<std::string
>& meta
)
50 bool Bind2Backend::setDomainMetadata(const DNSName
& name
, const std::string
& kind
, const std::vector
<std::string
>& meta
)
53 bool Bind2Backend::getDomainKeys(const DNSName
& name
, std::vector
<KeyData
>& keys
)
56 bool Bind2Backend::removeDomainKey(const DNSName
& name
, unsigned int id
)
59 bool Bind2Backend::addDomainKey(const DNSName
& name
, const KeyData
& key
, int64_t& id
)
62 bool Bind2Backend::activateDomainKey(const DNSName
& name
, unsigned int id
)
65 bool Bind2Backend::deactivateDomainKey(const DNSName
& name
, unsigned int id
)
68 bool Bind2Backend::getTSIGKey(const DNSName
& name
, DNSName
* algorithm
, string
* content
)
71 bool Bind2Backend::setTSIGKey(const DNSName
& name
, const DNSName
& algorithm
, const string
& content
)
74 bool Bind2Backend::deleteTSIGKey(const DNSName
& name
)
77 bool Bind2Backend::getTSIGKeys(std::vector
<struct TSIGKey
> &keys
)
80 void Bind2Backend::setupStatements()
83 void Bind2Backend::freeStatements()
88 #include "pdns/bind-dnssec.schema.sqlite3.sql.h"
89 #include "pdns/logger.hh"
90 #include "pdns/ssqlite3.hh"
92 #define ASSERT_ROW_COLUMNS(query, row, num) { if (row.size() != num) { throw PDNSException(std::string(query) + " returned wrong number of columns, expected " #num ", got " + std::to_string(row.size())); } }
94 void Bind2Backend::setupDNSSEC()
96 if(getArg("dnssec-db").empty() || d_hybrid
)
99 d_dnssecdb
= shared_ptr
<SSQLite3
>(new SSQLite3(getArg("dnssec-db")));
102 catch(SSqlException
& se
) {
103 // this error is meant to kill the server dead - it makes no sense to continue..
104 throw runtime_error("Error opening DNSSEC database in BIND backend: "+se
.txtReason());
107 d_dnssecdb
->setLog(::arg().mustDo("query-logging"));
110 void Bind2Backend::setupStatements()
112 d_getAllDomainMetadataQuery_stmt
= d_dnssecdb
->prepare("select kind, content from domainmetadata where domain=:domain",1);
113 d_getDomainMetadataQuery_stmt
= d_dnssecdb
->prepare("select content from domainmetadata where domain=:domain and kind=:kind",2);
114 d_deleteDomainMetadataQuery_stmt
= d_dnssecdb
->prepare("delete from domainmetadata where domain=:domain and kind=:kind",2);
115 d_insertDomainMetadataQuery_stmt
= d_dnssecdb
->prepare("insert into domainmetadata (domain, kind, content) values (:domain,:kind,:content)",3);
116 d_getDomainKeysQuery_stmt
= d_dnssecdb
->prepare("select id,flags, active, content from cryptokeys where domain=:domain",1);
117 d_deleteDomainKeyQuery_stmt
= d_dnssecdb
->prepare("delete from cryptokeys where domain=:domain and id=:key_id",2);
118 d_insertDomainKeyQuery_stmt
= d_dnssecdb
->prepare("insert into cryptokeys (domain, flags, active, content) values (:domain, :flags, :active, :content)", 4);
119 d_GetLastInsertedKeyIdQuery_stmt
= d_dnssecdb
->prepare("select last_insert_rowid()", 0);
120 d_activateDomainKeyQuery_stmt
= d_dnssecdb
->prepare("update cryptokeys set active=1 where domain=:domain and id=:key_id", 2);
121 d_deactivateDomainKeyQuery_stmt
= d_dnssecdb
->prepare("update cryptokeys set active=0 where domain=:domain and id=:key_id", 2);
122 d_getTSIGKeyQuery_stmt
= d_dnssecdb
->prepare("select algorithm, secret from tsigkeys where name=:key_name", 1);
123 d_setTSIGKeyQuery_stmt
= d_dnssecdb
->prepare("replace into tsigkeys (name,algorithm,secret) values(:key_name, :algorithm, :content)", 3);
124 d_deleteTSIGKeyQuery_stmt
= d_dnssecdb
->prepare("delete from tsigkeys where name=:key_name", 1);
125 d_getTSIGKeysQuery_stmt
= d_dnssecdb
->prepare("select name,algorithm,secret from tsigkeys", 0);
128 void Bind2Backend::release(SSqlStatement
** stmt
) {
133 void Bind2Backend::freeStatements()
135 d_getAllDomainMetadataQuery_stmt
.reset();
136 d_getDomainMetadataQuery_stmt
.reset();
137 d_deleteDomainMetadataQuery_stmt
.reset();
138 d_insertDomainMetadataQuery_stmt
.reset();
139 d_getDomainKeysQuery_stmt
.reset();
140 d_deleteDomainKeyQuery_stmt
.reset();
141 d_insertDomainKeyQuery_stmt
.reset();
142 d_GetLastInsertedKeyIdQuery_stmt
.reset();
143 d_activateDomainKeyQuery_stmt
.reset();
144 d_deactivateDomainKeyQuery_stmt
.reset();
145 d_getTSIGKeyQuery_stmt
.reset();
146 d_setTSIGKeyQuery_stmt
.reset();
147 d_deleteTSIGKeyQuery_stmt
.reset();
148 d_getTSIGKeysQuery_stmt
.reset();
151 bool Bind2Backend::doesDNSSEC()
153 return d_dnssecdb
|| d_hybrid
;
156 bool Bind2Backend::getNSEC3PARAM(const DNSName
& name
, NSEC3PARAMRecordContent
* ns3p
)
158 if(!d_dnssecdb
|| d_hybrid
)
163 getDomainMetadata(name
, "NSEC3PARAM", meta
);
167 return false; // No NSEC3 zone
169 static int maxNSEC3Iterations
=::arg().asNum("max-nsec3-iterations");
171 auto tmp
=std::dynamic_pointer_cast
<NSEC3PARAMRecordContent
>(DNSRecordContent::mastermake(QType::NSEC3PARAM
, 1, value
));
174 if (ns3p
->d_iterations
> maxNSEC3Iterations
) {
175 ns3p
->d_iterations
= maxNSEC3Iterations
;
176 g_log
<<Logger::Error
<<"Number of NSEC3 iterations for zone '"<<name
<<"' is above 'max-nsec3-iterations'. Value adjusted to: "<<maxNSEC3Iterations
<<endl
;
179 if (ns3p
->d_algorithm
!= 1) {
180 g_log
<<Logger::Error
<<"Invalid hash algorithm for NSEC3: '"<<std::to_string(ns3p
->d_algorithm
)<<"', setting to 1 for zone '"<<name
<<"'."<<endl
;
181 ns3p
->d_algorithm
= 1;
188 bool Bind2Backend::getAllDomainMetadata(const DNSName
& name
, std::map
<std::string
, std::vector
<std::string
> >& meta
)
190 if(!d_dnssecdb
|| d_hybrid
)
194 d_getAllDomainMetadataQuery_stmt
->
195 bind("domain", name
)->
198 SSqlStatement::row_t row
;
199 while(d_getAllDomainMetadataQuery_stmt
->hasNextRow()) {
200 d_getAllDomainMetadataQuery_stmt
->nextRow(row
);
201 meta
[row
[0]].push_back(row
[1]);
204 d_getAllDomainMetadataQuery_stmt
->reset();
206 catch(SSqlException
& se
) {
207 throw PDNSException("Error accessing DNSSEC database in BIND backend, getAllDomainMetadata(): "+se
.txtReason());
212 bool Bind2Backend::getDomainMetadata(const DNSName
& name
, const std::string
& kind
, std::vector
<std::string
>& meta
)
214 if(!d_dnssecdb
|| d_hybrid
)
218 d_getDomainMetadataQuery_stmt
->
219 bind("domain", name
)->
223 SSqlStatement::row_t row
;
224 while(d_getDomainMetadataQuery_stmt
->hasNextRow()) {
225 d_getDomainMetadataQuery_stmt
->nextRow(row
);
226 meta
.push_back(row
[0]);
229 d_getDomainMetadataQuery_stmt
->reset();
231 catch(SSqlException
& se
) {
232 throw PDNSException("Error accessing DNSSEC database in BIND backend, getDomainMetadata(): "+se
.txtReason());
237 bool Bind2Backend::setDomainMetadata(const DNSName
& name
, const std::string
& kind
, const std::vector
<std::string
>& meta
)
239 if(!d_dnssecdb
|| d_hybrid
)
243 d_deleteDomainMetadataQuery_stmt
->
244 bind("domain", name
)->
249 for(const auto& value
: meta
) {
250 d_insertDomainMetadataQuery_stmt
->
251 bind("domain", name
)->
253 bind("content", value
)->
259 catch(SSqlException
& se
) {
260 throw PDNSException("Error accessing DNSSEC database in BIND backend, setDomainMetadata(): "+se
.txtReason());
265 bool Bind2Backend::getDomainKeys(const DNSName
& name
, std::vector
<KeyData
>& keys
)
267 if(!d_dnssecdb
|| d_hybrid
)
271 d_getDomainKeysQuery_stmt
->
272 bind("domain", name
)->
276 SSqlStatement::row_t row
;
277 while(d_getDomainKeysQuery_stmt
->hasNextRow()) {
278 d_getDomainKeysQuery_stmt
->nextRow(row
);
279 kd
.id
= pdns_stou(row
[0]);
280 kd
.flags
= pdns_stou(row
[1]);
281 kd
.active
= (row
[2] == "1");
286 d_getDomainKeysQuery_stmt
->reset();
288 catch(SSqlException
& se
) {
289 throw PDNSException("Error accessing DNSSEC database in BIND backend, getDomainKeys(): "+se
.txtReason());
294 bool Bind2Backend::removeDomainKey(const DNSName
& name
, unsigned int id
)
296 if(!d_dnssecdb
|| d_hybrid
)
300 d_deleteDomainKeyQuery_stmt
->
301 bind("domain", name
)->
306 catch(SSqlException
& se
) {
307 throw PDNSException("Error accessing DNSSEC database in BIND backend, removeDomainKeys(): "+se
.txtReason());
312 bool Bind2Backend::addDomainKey(const DNSName
& name
, const KeyData
& key
, int64_t& id
)
314 if(!d_dnssecdb
|| d_hybrid
)
318 d_insertDomainKeyQuery_stmt
->
319 bind("domain", name
)->
320 bind("flags", key
.flags
)->
321 bind("active", key
.active
)->
322 bind("content", key
.content
)->
326 catch(SSqlException
& se
) {
327 throw PDNSException("Error accessing DNSSEC database in BIND backend, addDomainKey(): "+se
.txtReason());
331 d_GetLastInsertedKeyIdQuery_stmt
->execute();
332 if (!d_GetLastInsertedKeyIdQuery_stmt
->hasNextRow()) {
336 SSqlStatement::row_t row
;
337 d_GetLastInsertedKeyIdQuery_stmt
->nextRow(row
);
338 ASSERT_ROW_COLUMNS("get-last-inserted-key-id-query", row
, 1);
339 id
= std::stoi(row
[0]);
340 d_GetLastInsertedKeyIdQuery_stmt
->reset();
343 catch (SSqlException
&e
) {
351 bool Bind2Backend::activateDomainKey(const DNSName
& name
, unsigned int id
)
353 if(!d_dnssecdb
|| d_hybrid
)
357 d_activateDomainKeyQuery_stmt
->
358 bind("domain", name
)->
363 catch(SSqlException
& se
) {
364 throw PDNSException("Error accessing DNSSEC database in BIND backend, activateDomainKey(): "+se
.txtReason());
369 bool Bind2Backend::deactivateDomainKey(const DNSName
& name
, unsigned int id
)
371 if(!d_dnssecdb
|| d_hybrid
)
375 d_deactivateDomainKeyQuery_stmt
->
376 bind("domain", name
)->
381 catch(SSqlException
& se
) {
382 throw PDNSException("Error accessing DNSSEC database in BIND backend, deactivateDomainKey(): "+se
.txtReason());
387 bool Bind2Backend::getTSIGKey(const DNSName
& name
, DNSName
* algorithm
, string
* content
)
389 if(!d_dnssecdb
|| d_hybrid
)
393 d_getTSIGKeyQuery_stmt
->
394 bind("key_name", name
)->
397 SSqlStatement::row_t row
;
399 while(d_getTSIGKeyQuery_stmt
->hasNextRow()) {
400 d_getTSIGKeyQuery_stmt
->nextRow(row
);
401 if(row
.size() >= 2 && (algorithm
->empty() || *algorithm
== DNSName(row
[0]))) {
402 *algorithm
= DNSName(row
[0]);
407 d_getTSIGKeyQuery_stmt
->reset();
409 catch (SSqlException
&e
) {
410 throw PDNSException("Error accessing DNSSEC database in BIND backend, getTSIGKey(): "+e
.txtReason());
412 return !content
->empty();
415 bool Bind2Backend::setTSIGKey(const DNSName
& name
, const DNSName
& algorithm
, const string
& content
)
417 if(!d_dnssecdb
|| d_hybrid
)
421 d_setTSIGKeyQuery_stmt
->
422 bind("key_name", name
)->
423 bind("algorithm", algorithm
)->
424 bind("content", content
)->
428 catch (SSqlException
&e
) {
429 throw PDNSException("Error accessing DNSSEC database in BIND backend, setTSIGKey(): "+e
.txtReason());
434 bool Bind2Backend::deleteTSIGKey(const DNSName
& name
)
436 if(!d_dnssecdb
|| d_hybrid
)
440 d_deleteTSIGKeyQuery_stmt
->
441 bind("key_name", name
)->
445 catch (SSqlException
&e
) {
446 throw PDNSException("Error accessing DNSSEC database in BIND backend, deleteTSIGKey(): "+e
.txtReason());
451 bool Bind2Backend::getTSIGKeys(std::vector
< struct TSIGKey
> &keys
)
453 if(!d_dnssecdb
|| d_hybrid
)
457 d_getTSIGKeysQuery_stmt
->
460 SSqlStatement::row_t row
;
461 while(d_getTSIGKeysQuery_stmt
->hasNextRow()) {
462 d_getTSIGKeysQuery_stmt
->nextRow(row
);
464 key
.name
= DNSName(row
[0]);
465 key
.algorithm
= DNSName(row
[1]);
470 d_getTSIGKeysQuery_stmt
->reset();
472 catch (SSqlException
&e
) {
473 throw PDNSException("Error accessing DNSSEC database in BIND backend, getTSIGKeys(): "+e
.txtReason());
475 return !keys
.empty();