2 PowerDNS Versatile Database Driven Nameserver
3 Copyright (C) 2002-2011 PowerDNS.COM BV
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License version 2 as
7 published by the Free Software Foundation
9 Additionally, the license of this program contains a special
10 exception which allows to distribute the program in binary form when
11 it is linked against OpenSSL.
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 St, Fifth Floor, Boston, MA 02110-1301 USA
26 #include "pdns/dns.hh"
27 #include "pdns/dnsbackend.hh"
28 #include "gsqlbackend.hh"
29 #include "pdns/dnspacket.hh"
30 #include "pdns/ueberbackend.hh"
31 #include "pdns/pdnsexception.hh"
32 #include "pdns/logger.hh"
33 #include "pdns/arguments.hh"
34 #include "pdns/base32.hh"
35 #include "pdns/dnssecinfra.hh"
36 #include <boost/algorithm/string.hpp>
38 #include <boost/foreach.hpp>
39 #include <boost/format.hpp>
40 #include <boost/scoped_ptr.hpp>
42 GSQLBackend::GSQLBackend(const string
&mode
, const string
&suffix
)
44 setArgPrefix(mode
+suffix
);
46 d_logprefix
="["+mode
+"Backend"+suffix
+"] ";
50 d_dnssecQueries
= mustDo("dnssec");
52 catch (ArgException e
)
54 d_dnssecQueries
= false;
57 d_NoIdQuery
=getArg("basic-query");
58 d_IdQuery
=getArg("id-query");
59 d_ANYNoIdQuery
=getArg("any-query");
60 d_ANYIdQuery
=getArg("any-id-query");
62 d_listQuery
=getArg("list-query");
63 d_listSubZoneQuery
=getArg("list-subzone-query");
65 d_MasterOfDomainsZoneQuery
=getArg("master-zone-query");
66 d_InfoOfDomainsZoneQuery
=getArg("info-zone-query");
67 d_InfoOfAllSlaveDomainsQuery
=getArg("info-all-slaves-query");
68 d_SuperMasterInfoQuery
=getArg("supermaster-query");
69 d_GetSuperMasterIPs
=getArg("supermaster-name-to-ips");
70 d_InsertZoneQuery
=getArg("insert-zone-query");
71 d_InsertSlaveZoneQuery
=getArg("insert-slave-query");
72 d_InsertRecordQuery
=getArg("insert-record-query");
73 d_InsertEntQuery
=getArg("insert-ent-query");
74 d_UpdateMasterOfZoneQuery
=getArg("update-master-query");
75 d_UpdateKindOfZoneQuery
=getArg("update-kind-query");
76 d_UpdateSerialOfZoneQuery
=getArg("update-serial-query");
77 d_UpdateLastCheckofZoneQuery
=getArg("update-lastcheck-query");
78 d_UpdateAccountOfZoneQuery
=getArg("update-account-query");
79 d_ZoneLastChangeQuery
=getArg("zone-lastchange-query");
80 d_InfoOfAllMasterDomainsQuery
=getArg("info-all-master-query");
81 d_DeleteDomainQuery
=getArg("delete-domain-query");
82 d_DeleteZoneQuery
=getArg("delete-zone-query");
83 d_DeleteRRSetQuery
=getArg("delete-rrset-query");
84 d_DeleteNamesQuery
=getArg("delete-names-query");
85 d_getAllDomainsQuery
=getArg("get-all-domains-query");
87 d_removeEmptyNonTerminalsFromZoneQuery
= getArg("remove-empty-non-terminals-from-zone-query");
88 d_insertEmptyNonTerminalQuery
= getArg("insert-empty-non-terminal-query");
89 d_deleteEmptyNonTerminalQuery
= getArg("delete-empty-non-terminal-query");
91 d_ListCommentsQuery
= getArg("list-comments-query");
92 d_InsertCommentQuery
= getArg("insert-comment-query");
93 d_DeleteCommentRRsetQuery
= getArg("delete-comment-rrset-query");
94 d_DeleteCommentsQuery
= getArg("delete-comments-query");
96 d_InsertRecordOrderQuery
=getArg("insert-record-order-query");
97 d_InsertEntOrderQuery
=getArg("insert-ent-order-query");
99 d_firstOrderQuery
= getArg("get-order-first-query");
100 d_beforeOrderQuery
= getArg("get-order-before-query");
101 d_afterOrderQuery
= getArg("get-order-after-query");
102 d_lastOrderQuery
= getArg("get-order-last-query");
103 d_setOrderAuthQuery
= getArg("set-order-and-auth-query");
104 d_nullifyOrderNameAndUpdateAuthQuery
= getArg("nullify-ordername-and-update-auth-query");
105 d_nullifyOrderNameAndAuthQuery
= getArg("nullify-ordername-and-auth-query");
106 d_setAuthOnDsRecordQuery
= getArg("set-auth-on-ds-record-query");
108 d_AddDomainKeyQuery
= getArg("add-domain-key-query");
109 d_ListDomainKeysQuery
= getArg("list-domain-keys-query");
111 d_GetAllDomainMetadataQuery
= getArg("get-all-domain-metadata-query");
112 d_GetDomainMetadataQuery
= getArg("get-domain-metadata-query");
113 d_ClearDomainMetadataQuery
= getArg("clear-domain-metadata-query");
114 d_ClearDomainAllMetadataQuery
= getArg("clear-domain-all-metadata-query");
115 d_SetDomainMetadataQuery
= getArg("set-domain-metadata-query");
117 d_ActivateDomainKeyQuery
= getArg("activate-domain-key-query");
118 d_DeactivateDomainKeyQuery
= getArg("deactivate-domain-key-query");
119 d_RemoveDomainKeyQuery
= getArg("remove-domain-key-query");
120 d_ClearDomainAllKeysQuery
= getArg("clear-domain-all-keys-query");
122 d_getTSIGKeyQuery
= getArg("get-tsig-key-query");
123 d_setTSIGKeyQuery
= getArg("set-tsig-key-query");
124 d_deleteTSIGKeyQuery
= getArg("delete-tsig-key-query");
125 d_getTSIGKeysQuery
= getArg("get-tsig-keys-query");
128 d_NoIdQuery_stmt
= NULL
;
129 d_IdQuery_stmt
= NULL
;
130 d_ANYNoIdQuery_stmt
= NULL
;
131 d_ANYIdQuery_stmt
= NULL
;
132 d_listQuery_stmt
= NULL
;
133 d_listSubZoneQuery_stmt
= NULL
;
134 d_MasterOfDomainsZoneQuery_stmt
= NULL
;
135 d_InfoOfDomainsZoneQuery_stmt
= NULL
;
136 d_InfoOfAllSlaveDomainsQuery_stmt
= NULL
;
137 d_SuperMasterInfoQuery_stmt
= NULL
;
138 d_GetSuperMasterIPs_stmt
= NULL
;
139 d_InsertZoneQuery_stmt
= NULL
;
140 d_InsertSlaveZoneQuery_stmt
= NULL
;
141 d_InsertRecordQuery_stmt
= NULL
;
142 d_InsertEntQuery_stmt
= NULL
;
143 d_InsertRecordOrderQuery_stmt
= NULL
;
144 d_InsertEntOrderQuery_stmt
= NULL
;
145 d_UpdateMasterOfZoneQuery_stmt
= NULL
;
146 d_UpdateKindOfZoneQuery_stmt
= NULL
;
147 d_UpdateSerialOfZoneQuery_stmt
= NULL
;
148 d_UpdateLastCheckofZoneQuery_stmt
= NULL
;
149 d_UpdateAccountOfZoneQuery_stmt
= NULL
;
150 d_InfoOfAllMasterDomainsQuery_stmt
= NULL
;
151 d_DeleteDomainQuery_stmt
= NULL
;
152 d_DeleteZoneQuery_stmt
= NULL
;
153 d_DeleteRRSetQuery_stmt
= NULL
;
154 d_DeleteNamesQuery_stmt
= NULL
;
155 d_ZoneLastChangeQuery_stmt
= NULL
;
156 d_firstOrderQuery_stmt
= NULL
;
157 d_beforeOrderQuery_stmt
= NULL
;
158 d_afterOrderQuery_stmt
= NULL
;
159 d_lastOrderQuery_stmt
= NULL
;
160 d_setOrderAuthQuery_stmt
= NULL
;
161 d_nullifyOrderNameAndUpdateAuthQuery_stmt
= NULL
;
162 d_nullifyOrderNameAndAuthQuery_stmt
= NULL
;
163 d_nullifyOrderNameAndAuthENTQuery_stmt
= NULL
;
164 d_setAuthOnDsRecordQuery_stmt
= NULL
;
165 d_removeEmptyNonTerminalsFromZoneQuery_stmt
= NULL
;
166 d_insertEmptyNonTerminalQuery_stmt
= NULL
;
167 d_deleteEmptyNonTerminalQuery_stmt
= NULL
;
168 d_AddDomainKeyQuery_stmt
= NULL
;
169 d_ListDomainKeysQuery_stmt
= NULL
;
170 d_GetAllDomainMetadataQuery_stmt
= NULL
;
171 d_GetDomainMetadataQuery_stmt
= NULL
;
172 d_ClearDomainMetadataQuery_stmt
= NULL
;
173 d_ClearDomainAllMetadataQuery_stmt
= NULL
;
174 d_SetDomainMetadataQuery_stmt
= NULL
;
175 d_RemoveDomainKeyQuery_stmt
= NULL
;
176 d_ActivateDomainKeyQuery_stmt
= NULL
;
177 d_DeactivateDomainKeyQuery_stmt
= NULL
;
178 d_ClearDomainAllKeysQuery_stmt
= NULL
;
179 d_getTSIGKeyQuery_stmt
= NULL
;
180 d_setTSIGKeyQuery_stmt
= NULL
;
181 d_deleteTSIGKeyQuery_stmt
= NULL
;
182 d_getTSIGKeysQuery_stmt
= NULL
;
183 d_getAllDomainsQuery_stmt
= NULL
;
184 d_ListCommentsQuery_stmt
= NULL
;
185 d_InsertCommentQuery_stmt
= NULL
;
186 d_DeleteCommentRRsetQuery_stmt
= NULL
;
187 d_DeleteCommentsQuery_stmt
= NULL
;
190 void GSQLBackend::setNotified(uint32_t domain_id
, uint32_t serial
)
193 d_UpdateSerialOfZoneQuery_stmt
->
194 bind("serial", serial
)->
195 bind("domain_id", domain_id
)->
199 catch(SSqlException
&e
) {
200 throw PDNSException("GSQLBackend unable to refresh domain_id "+itoa(domain_id
)+": "+e
.txtReason());
204 void GSQLBackend::setFresh(uint32_t domain_id
)
207 d_UpdateLastCheckofZoneQuery_stmt
->
208 bind("last_check", time(0))->
209 bind("domain_id", domain_id
)->
213 catch (SSqlException
&e
) {
214 throw PDNSException("GSQLBackend unable to refresh domain_id "+itoa(domain_id
)+": "+e
.txtReason());
218 bool GSQLBackend::isMaster(const string
&domain
, const string
&ip
)
221 d_MasterOfDomainsZoneQuery_stmt
->
222 bind("domain", domain
)->
224 getResult(d_result
)->
227 catch (SSqlException
&e
) {
228 throw PDNSException("GSQLBackend unable to retrieve list of master domains: "+e
.txtReason());
231 if(!d_result
.empty()) {
233 // we can have multiple masters separated by commas
234 vector
<string
> masters
;
235 stringtok(masters
, d_result
[0][0], " ,\t");
237 BOOST_FOREACH(const string master
, masters
) {
238 const ComboAddress
caMaster(master
);
239 if(ip
== caMaster
.toString())
244 // no matching master
248 bool GSQLBackend::setMaster(const string
&domain
, const string
&ip
)
251 d_UpdateMasterOfZoneQuery_stmt
->
253 bind("domain", toLower(domain
))->
257 catch (SSqlException
&e
) {
258 throw PDNSException("GSQLBackend unable to set master of domain \""+domain
+"\": "+e
.txtReason());
263 bool GSQLBackend::setKind(const string
&domain
, const DomainInfo::DomainKind kind
)
266 d_UpdateKindOfZoneQuery_stmt
->
267 bind("kind", toUpper(DomainInfo::getKindString(kind
)))->
268 bind("domain", toLower(domain
))->
272 catch (SSqlException
&e
) {
273 throw PDNSException("GSQLBackend unable to set kind of domain \""+domain
+"\": "+e
.txtReason());
278 bool GSQLBackend::setAccount(const string
&domain
, const string
&account
)
281 d_UpdateAccountOfZoneQuery_stmt
->
282 bind("account", account
)->
283 bind("domain", toLower(domain
))->
287 catch (SSqlException
&e
) {
288 throw PDNSException("GSQLBackend unable to set account of domain \""+domain
+"\": "+e
.txtReason());
293 bool GSQLBackend::getDomainInfo(const string
&domain
, DomainInfo
&di
)
295 /* fill DomainInfo from database info:
296 id,name,master IP(s),last_check,notified_serial,type,account */
298 d_InfoOfDomainsZoneQuery_stmt
->
299 bind("domain", toLower(domain
))->
301 getResult(d_result
)->
304 catch(SSqlException
&e
) {
305 throw PDNSException("GSQLBackend unable to retrieve information about a domain: "+e
.txtReason());
308 int numanswers
=d_result
.size();
312 di
.id
=atol(d_result
[0][0].c_str());
313 di
.zone
=d_result
[0][1];
314 stringtok(di
.masters
, d_result
[0][2], " ,\t");
315 di
.last_check
=atol(d_result
[0][3].c_str());
316 di
.notified_serial
= atol(d_result
[0][4].c_str());
317 string type
=d_result
[0][5];
318 di
.account
=d_result
[0][6];
324 if(!getSOA(domain
,sd
))
325 L
<<Logger::Notice
<<"No serial for '"<<domain
<<"' found - zone is missing?"<<endl
;
327 di
.serial
= sd
.serial
;
329 catch(PDNSException
&ae
){
330 L
<<Logger::Error
<<"Error retrieving serial for '"<<domain
<<"': "<<ae
.reason
<<endl
;
333 di
.kind
= DomainInfo::stringToKind(type
);
338 void GSQLBackend::getUnfreshSlaveInfos(vector
<DomainInfo
> *unfreshDomains
)
340 /* list all domains that need refreshing for which we are slave, and insert into SlaveDomain:
341 id,name,master IP,serial */
343 d_InfoOfAllSlaveDomainsQuery_stmt
->
345 getResult(d_result
)->
348 catch (SSqlException
&e
) {
349 throw PDNSException("GSQLBackend unable to retrieve list of slave domains: "+e
.txtReason());
352 vector
<DomainInfo
> allSlaves
;
353 int numanswers
=d_result
.size();
354 for(int n
=0;n
<numanswers
;++n
) { // id,name,master,last_check
356 sd
.id
=atol(d_result
[n
][0].c_str());
357 sd
.zone
=d_result
[n
][1];
358 stringtok(sd
.masters
, d_result
[n
][2], ", \t");
359 sd
.last_check
=atol(d_result
[n
][3].c_str());
361 sd
.kind
=DomainInfo::Slave
;
362 allSlaves
.push_back(sd
);
365 for(vector
<DomainInfo
>::iterator i
=allSlaves
.begin();i
!=allSlaves
.end();++i
) {
369 getSOA(i
->zone
,sdata
);
370 if((time_t)(i
->last_check
+sdata
.refresh
) < time(0)) {
371 i
->serial
=sdata
.serial
;
372 unfreshDomains
->push_back(*i
);
377 void GSQLBackend::getUpdatedMasters(vector
<DomainInfo
> *updatedDomains
)
379 /* list all domains that need notifications for which we are master, and insert into updatedDomains
380 id,name,master IP,serial */
382 d_InfoOfAllMasterDomainsQuery_stmt
->
384 getResult(d_result
)->
387 catch(SSqlException
&e
) {
388 throw PDNSException("GSQLBackend unable to retrieve list of master domains: "+e
.txtReason());
391 vector
<DomainInfo
> allMasters
;
392 int numanswers
=d_result
.size();
393 for(int n
=0;n
<numanswers
;++n
) { // id,name,master,last_check
395 sd
.id
=atol(d_result
[n
][0].c_str());
396 sd
.zone
=d_result
[n
][1];
397 sd
.last_check
=atol(d_result
[n
][3].c_str());
398 sd
.notified_serial
=atoi(d_result
[n
][4].c_str());
400 sd
.kind
=DomainInfo::Master
;
401 allMasters
.push_back(sd
);
404 for(vector
<DomainInfo
>::iterator i
=allMasters
.begin();i
!=allMasters
.end();++i
) {
408 getSOA(i
->zone
,sdata
);
409 if(i
->notified_serial
!=sdata
.serial
) {
410 i
->serial
=sdata
.serial
;
411 updatedDomains
->push_back(*i
);
416 bool GSQLBackend::updateDNSSECOrderAndAuth(uint32_t domain_id
, const std::string
& zonename
, const std::string
& qname
, bool auth
)
420 string ins
=toLower(labelReverse(makeRelative(qname
, zonename
)));
421 return this->updateDNSSECOrderAndAuthAbsolute(domain_id
, qname
, ins
, auth
);
424 bool GSQLBackend::updateDNSSECOrderAndAuthAbsolute(uint32_t domain_id
, const std::string
& qname
, const std::string
& ordername
, bool auth
)
430 d_setOrderAuthQuery_stmt
->
431 bind("ordername", ordername
)->
433 bind("qname", qname
)->
434 bind("domain_id", domain_id
)->
438 catch(SSqlException
&e
) {
439 throw PDNSException("GSQLBackend unable to update ordername/auth for domain_id "+itoa(domain_id
)+": "+e
.txtReason());
444 bool GSQLBackend::nullifyDNSSECOrderNameAndUpdateAuth(uint32_t domain_id
, const std::string
& qname
, bool auth
)
450 d_nullifyOrderNameAndUpdateAuthQuery_stmt
->
452 bind("domain_id", domain_id
)->
453 bind("qname", qname
)->
457 catch(SSqlException
&e
) {
458 throw PDNSException("GSQLBackend unable to nullify ordername and update auth for domain_id "+itoa(domain_id
)+": "+e
.txtReason());
463 bool GSQLBackend::nullifyDNSSECOrderNameAndAuth(uint32_t domain_id
, const std::string
& qname
, const std::string
& type
)
469 d_nullifyOrderNameAndAuthQuery_stmt
->
470 bind("qname", qname
)->
471 bind("qtype", type
)->
472 bind("domain_id", domain_id
)->
476 catch(SSqlException
&e
) {
477 throw PDNSException("GSQLBackend unable to nullify ordername/auth for domain_id "+itoa(domain_id
)+": "+e
.txtReason());
482 bool GSQLBackend::setDNSSECAuthOnDsRecord(uint32_t domain_id
, const std::string
& qname
)
488 d_setAuthOnDsRecordQuery_stmt
->
489 bind("domain_id", domain_id
)->
490 bind("qname", qname
)->
494 catch(SSqlException
&e
) {
495 throw PDNSException("GSQLBackend unable to set auth on DS record "+qname
+" for domain_id "+itoa(domain_id
)+": "+e
.txtReason());
500 bool GSQLBackend::updateEmptyNonTerminals(uint32_t domain_id
, const std::string
& zonename
, set
<string
>& insert
, set
<string
>& erase
, bool remove
)
504 d_removeEmptyNonTerminalsFromZoneQuery_stmt
->
505 bind("domain_id", domain_id
)->
509 catch (SSqlException
&e
) {
510 throw PDNSException("GSQLBackend unable to delete empty non-terminal records from domain_id "+itoa(domain_id
)+": "+e
.txtReason());
516 BOOST_FOREACH(const string qname
, erase
) {
518 d_deleteEmptyNonTerminalQuery_stmt
->
519 bind("domain_id", domain_id
)->
520 bind("qname", qname
)->
524 catch (SSqlException
&e
) {
525 throw PDNSException("GSQLBackend unable to delete empty non-terminal rr "+qname
+" from domain_id "+itoa(domain_id
)+": "+e
.txtReason());
531 BOOST_FOREACH(const string qname
, insert
) {
533 d_insertEmptyNonTerminalQuery_stmt
->
534 bind("domain_id", domain_id
)->
535 bind("qname", qname
)->
539 catch (SSqlException
&e
) {
540 throw PDNSException("GSQLBackend unable to insert empty non-terminal rr "+qname
+" in domain_id "+itoa(domain_id
)+": "+e
.txtReason());
548 bool GSQLBackend::doesDNSSEC()
550 return d_dnssecQueries
;
553 bool GSQLBackend::getBeforeAndAfterNamesAbsolute(uint32_t id
, const std::string
& qname
, std::string
& unhashed
, std::string
& before
, std::string
& after
)
557 // cerr<<"gsql before/after called for id="<<id<<", qname='"<<qname<<"'"<<endl;
559 string lcqname
=toLower(qname
);
561 SSqlStatement::row_t row
;
563 d_afterOrderQuery_stmt
->
564 bind("ordername", lcqname
)->
565 bind("domain_id", id
)->
567 while(d_afterOrderQuery_stmt
->hasNextRow()) {
568 d_afterOrderQuery_stmt
->nextRow(row
);
571 d_afterOrderQuery_stmt
->reset();
573 catch(SSqlException
&e
) {
574 throw PDNSException("GSQLBackend unable to find before/after (after) for domain_id "+itoa(id
)+": "+e
.txtReason());
577 if(after
.empty() && !lcqname
.empty()) {
579 d_firstOrderQuery_stmt
->
580 bind("domain_id", id
)->
582 while(d_firstOrderQuery_stmt
->hasNextRow()) {
583 d_firstOrderQuery_stmt
->nextRow(row
);
586 d_firstOrderQuery_stmt
->reset();
588 catch(SSqlException
&e
) {
589 throw PDNSException("GSQLBackend unable to find before/after (first) for domain_id "+itoa(id
)+": "+e
.txtReason());
593 if (before
.empty()) {
597 d_beforeOrderQuery_stmt
->
598 bind("ordername", lcqname
)->
599 bind("domain_id", id
)->
601 while(d_beforeOrderQuery_stmt
->hasNextRow()) {
602 d_beforeOrderQuery_stmt
->nextRow(row
);
606 d_beforeOrderQuery_stmt
->reset();
608 catch(SSqlException
&e
) {
609 throw PDNSException("GSQLBackend unable to find before/after (before) for domain_id "+itoa(id
)+": "+e
.txtReason());
612 if(! unhashed
.empty())
614 // cerr<<"unhashed="<<unhashed<<",before="<<before<<", after="<<after<<endl;
619 d_lastOrderQuery_stmt
->
620 bind("domain_id", id
)->
622 while(d_lastOrderQuery_stmt
->hasNextRow()) {
623 d_lastOrderQuery_stmt
->nextRow(row
);
627 d_lastOrderQuery_stmt
->reset();
629 catch(SSqlException
&e
) {
630 throw PDNSException("GSQLBackend unable to find before/after (last) for domain_id "+itoa(id
)+": "+e
.txtReason());
639 int GSQLBackend::addDomainKey(const string
& name
, const KeyData
& key
)
645 d_AddDomainKeyQuery_stmt
->
646 bind("flags", key
.flags
)->
647 bind("active", key
.active
)->
648 bind("content", key
.content
)->
649 bind("domain", toLower(name
))->
653 catch (SSqlException
&e
) {
654 throw PDNSException("GSQLBackend unable to store key: "+e
.txtReason());
656 return 1; // XXX FIXME, no idea how to get the id
659 bool GSQLBackend::activateDomainKey(const string
& name
, unsigned int id
)
665 d_ActivateDomainKeyQuery_stmt
->
666 bind("domain", toLower(name
))->
671 catch (SSqlException
&e
) {
672 throw PDNSException("GSQLBackend unable to activate key: "+e
.txtReason());
677 bool GSQLBackend::deactivateDomainKey(const string
& name
, unsigned int id
)
683 d_DeactivateDomainKeyQuery_stmt
->
684 bind("domain", toLower(name
))->
689 catch (SSqlException
&e
) {
690 throw PDNSException("GSQLBackend unable to deactivate key: "+e
.txtReason());
695 bool GSQLBackend::removeDomainKey(const string
& name
, unsigned int id
)
701 d_RemoveDomainKeyQuery_stmt
->
702 bind("domain", toLower(name
))->
707 catch (SSqlException
&e
) {
708 throw PDNSException("GSQLBackend unable to remove key: "+e
.txtReason());
713 bool GSQLBackend::getTSIGKey(const string
& name
, string
* algorithm
, string
* content
)
716 d_getTSIGKeyQuery_stmt
->
717 bind("key_name", toLower(name
))->
720 SSqlStatement::row_t row
;
723 while(d_getTSIGKeyQuery_stmt
->hasNextRow()) {
724 d_getTSIGKeyQuery_stmt
->nextRow(row
);
725 if(row
.size() >= 2 && (algorithm
->empty() || pdns_iequals(*algorithm
, row
[0]))) {
731 d_getTSIGKeyQuery_stmt
->reset();
733 catch (SSqlException
&e
) {
734 throw PDNSException("GSQLBackend unable to retrieve named TSIG key: "+e
.txtReason());
737 return !content
->empty();
740 bool GSQLBackend::setTSIGKey(const string
& name
, const string
& algorithm
, const string
& content
)
743 d_setTSIGKeyQuery_stmt
->
744 bind("key_name", toLower(name
))->
745 bind("algorithm", toLower(algorithm
))->
746 bind("content", content
)->
750 catch (SSqlException
&e
) {
751 throw PDNSException("GSQLBackend unable to store named TSIG key: "+e
.txtReason());
756 bool GSQLBackend::deleteTSIGKey(const string
& name
)
759 d_deleteTSIGKeyQuery_stmt
->
760 bind("key_name", toLower(name
))->
764 catch (SSqlException
&e
) {
765 throw PDNSException("GSQLBackend unable to store named TSIG key: "+e
.txtReason());
770 bool GSQLBackend::getTSIGKeys(std::vector
< struct TSIGKey
> &keys
)
773 d_getTSIGKeysQuery_stmt
->
776 SSqlStatement::row_t row
;
778 while(d_getTSIGKeysQuery_stmt
->hasNextRow()) {
779 d_getTSIGKeysQuery_stmt
->nextRow(row
);
782 key
.algorithm
= row
[1];
787 d_getTSIGKeysQuery_stmt
->reset();
789 catch (SSqlException
&e
) {
790 throw PDNSException("GSQLBackend unable to retrieve TSIG keys: "+e
.txtReason());
796 bool GSQLBackend::getDomainKeys(const string
& name
, unsigned int kind
, std::vector
<KeyData
>& keys
)
802 d_ListDomainKeysQuery_stmt
->
803 bind("domain", toLower(name
))->
806 SSqlStatement::row_t row
;
807 // "select id, kind, active, content from domains, cryptokeys where domain_id=domains.id and name='%s'";
809 while(d_ListDomainKeysQuery_stmt
->hasNextRow()) {
810 d_ListDomainKeysQuery_stmt
->nextRow(row
);
811 //~ BOOST_FOREACH(const std::string& val, row) {
812 //~ cerr<<"'"<<val<<"'"<<endl;
814 kd
.id
= atoi(row
[0].c_str());
815 kd
.flags
= atoi(row
[1].c_str());
816 kd
.active
= atoi(row
[2].c_str());
821 d_ListDomainKeysQuery_stmt
->reset();
823 catch (SSqlException
&e
) {
824 throw PDNSException("GSQLBackend unable to list keys: "+e
.txtReason());
830 void GSQLBackend::alsoNotifies(const string
&domain
, set
<string
> *ips
)
833 getDomainMetadata(domain
, "ALSO-NOTIFY", meta
);
834 BOOST_FOREACH(string
& str
, meta
) {
839 bool GSQLBackend::getAllDomainMetadata(const string
& name
, std::map
<std::string
, std::vector
<std::string
> >& meta
)
842 d_GetAllDomainMetadataQuery_stmt
->
843 bind("domain", toLower(name
))->
846 SSqlStatement::row_t row
;
848 while(d_GetAllDomainMetadataQuery_stmt
->hasNextRow()) {
849 d_GetAllDomainMetadataQuery_stmt
->nextRow(row
);
850 if (!isDnssecDomainMetadata(row
[0]))
851 meta
[row
[0]].push_back(row
[1]);
854 d_GetAllDomainMetadataQuery_stmt
->reset();
856 catch (SSqlException
&e
) {
857 throw PDNSException("GSQLBackend unable to list metadata: "+e
.txtReason());
864 bool GSQLBackend::getDomainMetadata(const string
& name
, const std::string
& kind
, std::vector
<std::string
>& meta
)
866 if(!d_dnssecQueries
&& isDnssecDomainMetadata(kind
))
870 d_GetDomainMetadataQuery_stmt
->
871 bind("domain", toLower(name
))->
875 SSqlStatement::row_t row
;
877 while(d_GetDomainMetadataQuery_stmt
->hasNextRow()) {
878 d_GetDomainMetadataQuery_stmt
->nextRow(row
);
879 meta
.push_back(row
[0]);
882 d_GetDomainMetadataQuery_stmt
->reset();
884 catch (SSqlException
&e
) {
885 throw PDNSException("GSQLBackend unable to list metadata: "+e
.txtReason());
891 bool GSQLBackend::setDomainMetadata(const string
& name
, const std::string
& kind
, const std::vector
<std::string
>& meta
)
893 if(!d_dnssecQueries
&& isDnssecDomainMetadata(kind
))
897 d_ClearDomainMetadataQuery_stmt
->
898 bind("domain", toLower(name
))->
903 BOOST_FOREACH(const std::string
& value
, meta
) {
904 d_SetDomainMetadataQuery_stmt
->
906 bind("content", value
)->
907 bind("domain", toLower(name
))->
913 catch (SSqlException
&e
) {
914 throw PDNSException("GSQLBackend unable to store metadata key: "+e
.txtReason());
920 void GSQLBackend::lookup(const QType
&qtype
,const string
&qname
, DNSPacket
*pkt_p
, int domain_id
)
922 string lcqname
=toLower(qname
);
925 if(qtype
.getCode()!=QType::ANY
) {
927 d_query_stmt
= d_NoIdQuery_stmt
;
929 bind("qtype", qtype
.getName())->
930 bind("qname", lcqname
);
932 d_query_stmt
= d_IdQuery_stmt
;
934 bind("qtype", qtype
.getName())->
935 bind("qname", lcqname
)->
936 bind("domain_id", domain_id
);
941 d_query_stmt
= d_ANYNoIdQuery_stmt
;
943 bind("qname", lcqname
);
945 d_query_stmt
= d_ANYIdQuery_stmt
;
947 bind("qname", lcqname
)->
948 bind("domain_id", domain_id
);
955 catch(SSqlException
&e
) {
956 throw PDNSException("GSQLBackend lookup query:"+e
.txtReason());
962 bool GSQLBackend::list(const string
&target
, int domain_id
, bool include_disabled
)
964 DLOG(L
<<"GSQLBackend constructing handle for list of domain id '"<<domain_id
<<"'"<<endl
);
967 d_query_stmt
= d_listQuery_stmt
;
969 bind("include_disabled", (int)include_disabled
)->
970 bind("domain_id", domain_id
)->
973 catch(SSqlException
&e
) {
974 throw PDNSException("GSQLBackend list query: "+e
.txtReason());
981 bool GSQLBackend::listSubZone(const string
&zone
, int domain_id
) {
982 string wildzone
= "%." + zone
;
985 d_query_stmt
= d_listSubZoneQuery_stmt
;
988 bind("wildzone", wildzone
)->
989 bind("domain_id", domain_id
)->
992 catch(SSqlException
&e
) {
993 throw PDNSException("GSQLBackend listSubZone query: "+e
.txtReason());
999 bool GSQLBackend::get(DNSResourceRecord
&r
)
1001 // L << "GSQLBackend get() was called for "<<qtype.getName() << " record: ";
1002 SSqlStatement::row_t row
;
1003 if(d_query_stmt
->hasNextRow()) {
1005 d_query_stmt
->nextRow(row
);
1006 } catch (SSqlException
&e
) {
1007 throw PDNSException("GSQLBackend get: "+e
.txtReason());
1010 r
.ttl
= ::arg().asNum( "default-ttl" );
1012 r
.ttl
=atol(row
[1].c_str());
1013 if(!d_qname
.empty())
1019 if (r
.qtype
==QType::MX
|| r
.qtype
==QType::SRV
)
1020 r
.content
=row
[2]+" "+row
[0];
1027 r
.auth
= !row
[7].empty() && row
[7][0]=='1';
1031 r
.disabled
= !row
[5].empty() && row
[5][0]=='1';
1033 r
.domain_id
=atoi(row
[4].c_str());
1038 d_query_stmt
->reset();
1039 } catch (SSqlException
&e
) {
1040 throw PDNSException("GSQLBackend get: "+e
.txtReason());
1042 d_query_stmt
= NULL
;
1046 bool GSQLBackend::superMasterBackend(const string
&ip
, const string
&domain
, const vector
<DNSResourceRecord
>&nsset
, string
*nameserver
, string
*account
, DNSBackend
**ddb
)
1048 // check if we know the ip/ns couple in the database
1049 for(vector
<DNSResourceRecord
>::const_iterator i
=nsset
.begin();i
!=nsset
.end();++i
) {
1051 d_SuperMasterInfoQuery_stmt
->
1053 bind("nameserver", i
->content
)->
1055 getResult(d_result
)->
1058 catch (SSqlException
&e
) {
1059 throw PDNSException("GSQLBackend unable to search for a domain: "+e
.txtReason());
1062 if(!d_result
.empty()) {
1063 *nameserver
=i
->content
;
1064 *account
=d_result
[0][0];
1072 bool GSQLBackend::createDomain(const string
&domain
)
1075 d_InsertZoneQuery_stmt
->
1076 bind("domain", toLower(domain
))->
1080 catch(SSqlException
&e
) {
1081 throw PDNSException("Database error trying to insert new domain '"+domain
+"': "+ e
.txtReason());
1086 bool GSQLBackend::createSlaveDomain(const string
&ip
, const string
&domain
, const string
&nameserver
, const string
&account
)
1091 if (!nameserver
.empty()) {
1092 // figure out all IP addresses for the master
1093 d_GetSuperMasterIPs_stmt
->
1094 bind("nameserver", nameserver
)->
1095 bind("account", account
)->
1097 getResult(d_result
)->
1099 if (!d_result
.empty()) {
1100 // collect all IP addresses
1102 BOOST_FOREACH(SSqlStatement::row_t
& row
, d_result
) {
1103 if (account
== row
[1])
1104 tmp
.push_back(row
[0]);
1106 // set them as domain's masters, comma separated
1107 masters
= boost::join(tmp
, ", ");
1110 d_InsertSlaveZoneQuery_stmt
->
1111 bind("domain", toLower(domain
))->
1112 bind("masters", masters
)->
1113 bind("account", account
)->
1117 catch(SSqlException
&e
) {
1118 throw PDNSException("Database error trying to insert new slave domain '"+domain
+"': "+ e
.txtReason());
1123 bool GSQLBackend::deleteDomain(const string
&domain
)
1126 if (!getDomainInfo(domain
, di
)) {
1131 d_DeleteZoneQuery_stmt
->
1132 bind("domain_id", di
.id
)->
1135 d_ClearDomainAllMetadataQuery_stmt
->
1136 bind("domain", toLower(domain
))->
1139 d_ClearDomainAllKeysQuery_stmt
->
1140 bind("domain", toLower(domain
))->
1143 d_DeleteCommentsQuery_stmt
->
1144 bind("domain_id", di
.id
)->
1147 d_DeleteDomainQuery_stmt
->
1148 bind("domain", toLower(domain
))->
1152 catch(SSqlException
&e
) {
1153 throw PDNSException("Database error trying to delete domain '"+domain
+"': "+ e
.txtReason());
1158 void GSQLBackend::getAllDomains(vector
<DomainInfo
> *domains
, bool include_disabled
)
1160 DLOG(L
<<"GSQLBackend retrieving all domains."<<endl
);
1163 d_getAllDomainsQuery_stmt
->
1164 bind("include_disabled", (int)include_disabled
)->
1167 SSqlStatement::row_t row
;
1168 while (d_getAllDomainsQuery_stmt
->hasNextRow()) {
1169 d_getAllDomainsQuery_stmt
->nextRow(row
);
1171 di
.id
= atol(row
[0].c_str());
1174 if (!row
[4].empty()) {
1175 stringtok(di
.masters
, row
[4], " ,\t");
1179 fillSOAData(row
[2], sd
);
1180 di
.serial
= sd
.serial
;
1181 di
.notified_serial
= atol(row
[5].c_str());
1182 di
.last_check
= atol(row
[6].c_str());
1183 di
.account
= row
[7];
1185 if (pdns_iequals(row
[3], "MASTER"))
1186 di
.kind
= DomainInfo::Master
;
1187 else if (pdns_iequals(row
[3], "SLAVE"))
1188 di
.kind
= DomainInfo::Slave
;
1190 di
.kind
= DomainInfo::Native
;
1194 domains
->push_back(di
);
1196 d_getAllDomainsQuery_stmt
->reset();
1198 catch (SSqlException
&e
) {
1199 throw PDNSException("Database error trying to retrieve all domains:" + e
.txtReason());
1203 bool GSQLBackend::replaceRRSet(uint32_t domain_id
, const string
& qname
, const QType
& qt
, const vector
<DNSResourceRecord
>& rrset
)
1206 if (qt
!= QType::ANY
) {
1207 d_DeleteRRSetQuery_stmt
->
1208 bind("domain_id", domain_id
)->
1209 bind("qname", qname
)->
1210 bind("qtype", qt
.getName())->
1214 d_DeleteNamesQuery_stmt
->
1215 bind("domain_id", domain_id
)->
1216 bind("qname", qname
)->
1221 catch (SSqlException
&e
) {
1222 throw PDNSException("GSQLBackend unable to delete RRSet: "+e
.txtReason());
1225 if (rrset
.empty()) {
1227 d_DeleteCommentRRsetQuery_stmt
->
1228 bind("domain_id", domain_id
)->
1229 bind("qname", qname
)->
1230 bind("qtype", qt
.getName())->
1234 catch (SSqlException
&e
) {
1235 throw PDNSException("GSQLBackend unable to delete comment: "+e
.txtReason());
1238 BOOST_FOREACH(const DNSResourceRecord
& rr
, rrset
) {
1245 bool GSQLBackend::feedRecord(const DNSResourceRecord
&r
, string
*ordername
)
1248 string
content(r
.content
);
1249 if (r
.qtype
== QType::MX
|| r
.qtype
== QType::SRV
) {
1250 prio
=atoi(content
.c_str());
1251 string::size_type pos
= content
.find_first_not_of("0123456789");
1252 if(pos
!= string::npos
)
1253 boost::erase_head(content
, pos
);
1258 if(d_dnssecQueries
&& ordername
)
1260 d_InsertRecordOrderQuery_stmt
->
1261 bind("content",content
)->
1263 bind("priority",prio
)->
1264 bind("qtype",r
.qtype
.getName())->
1265 bind("domain_id",r
.domain_id
)->
1266 bind("disabled",r
.disabled
)->
1267 bind("qname",toLower(r
.qname
));
1268 if (ordername
== NULL
)
1269 d_InsertRecordOrderQuery_stmt
->bindNull("ordername");
1271 d_InsertRecordOrderQuery_stmt
->bind("ordername",*ordername
);
1272 d_InsertRecordOrderQuery_stmt
->
1273 bind("auth",r
.auth
)->
1279 d_InsertRecordQuery_stmt
->
1280 bind("content",content
)->
1282 bind("priority",prio
)->
1283 bind("qtype",r
.qtype
.getName())->
1284 bind("domain_id",r
.domain_id
)->
1285 bind("disabled",r
.disabled
)->
1286 bind("qname",toLower(r
.qname
))->
1287 bind("auth", (r
.auth
|| !d_dnssecQueries
))->
1292 catch (SSqlException
&e
) {
1293 throw PDNSException("GSQLBackend unable to feed record: "+e
.txtReason());
1295 return true; // XXX FIXME this API should not return 'true' I think -ahu
1298 bool GSQLBackend::feedEnts(int domain_id
, map
<string
,bool>& nonterm
)
1301 pair
<string
,bool> nt
;
1303 BOOST_FOREACH(nt
, nonterm
) {
1305 d_InsertEntQuery_stmt
->
1306 bind("domain_id",domain_id
)->
1307 bind("qname",toLower(nt
.first
))->
1308 bind("auth",(nt
.second
|| !d_dnssecQueries
))->
1312 catch (SSqlException
&e
) {
1313 throw PDNSException("GSQLBackend unable to feed empty non-terminal: "+e
.txtReason());
1319 bool GSQLBackend::feedEnts3(int domain_id
, const string
&domain
, map
<string
,bool> &nonterm
, unsigned int times
, const string
&salt
, bool narrow
)
1321 if(!d_dnssecQueries
)
1325 pair
<string
,bool> nt
;
1327 BOOST_FOREACH(nt
, nonterm
) {
1329 if(narrow
|| !nt
.second
) {
1330 d_InsertEntQuery_stmt
->
1331 bind("domain_id",domain_id
)->
1332 bind("qname",toLower(nt
.first
))->
1333 bind("auth", nt
.second
)->
1337 ordername
=toBase32Hex(hashQNameWithSalt(times
, salt
, nt
.first
));
1338 d_InsertEntOrderQuery_stmt
->
1339 bind("domain_id",domain_id
)->
1340 bind("qname",toLower(nt
.first
))->
1341 bind("ordername",toLower(ordername
))->
1342 bind("auth",nt
.second
)->
1347 catch (SSqlException
&e
) {
1348 throw PDNSException("GSQLBackend unable to feed empty non-terminal: "+e
.txtReason());
1354 bool GSQLBackend::startTransaction(const string
&domain
, int domain_id
)
1357 d_db
->startTransaction();
1358 if(domain_id
>= 0) {
1359 d_DeleteZoneQuery_stmt
->
1360 bind("domain_id", domain_id
)->
1365 catch (SSqlException
&e
) {
1366 throw PDNSException("Database failed to start transaction: "+e
.txtReason());
1372 bool GSQLBackend::commitTransaction()
1377 catch (SSqlException
&e
) {
1378 throw PDNSException("Database failed to commit transaction: "+e
.txtReason());
1383 bool GSQLBackend::abortTransaction()
1388 catch(SSqlException
&e
) {
1389 throw PDNSException("Database failed to abort transaction: "+string(e
.txtReason()));
1394 bool GSQLBackend::calculateSOASerial(const string
& domain
, const SOAData
& sd
, time_t& serial
)
1396 if (d_ZoneLastChangeQuery
.empty()) {
1397 // query not set => fall back to default impl
1398 return DNSBackend::calculateSOASerial(domain
, sd
, serial
);
1402 d_ZoneLastChangeQuery_stmt
->
1403 bind("domain_id", sd
.domain_id
)->
1405 getResult(d_result
)->
1408 catch (const SSqlException
& e
) {
1409 //DLOG(L<<"GSQLBackend unable to calculate SOA serial: " << e.txtReason()<<endl);
1413 if (!d_result
.empty()) {
1414 serial
= atol(d_result
[0][0].c_str());
1421 bool GSQLBackend::listComments(const uint32_t domain_id
)
1424 d_query_stmt
= d_ListCommentsQuery_stmt
;
1426 bind("domain_id", domain_id
)->
1429 catch(SSqlException
&e
) {
1430 throw PDNSException("GSQLBackend list comments query: "+e
.txtReason());
1436 bool GSQLBackend::getComment(Comment
& comment
)
1438 SSqlStatement::row_t row
;
1440 if (!d_query_stmt
->hasNextRow()) {
1442 d_query_stmt
->reset();
1443 } catch(SSqlException
&e
) {
1444 throw PDNSException("GSQLBackend comment get: "+e
.txtReason());
1446 d_query_stmt
= NULL
;
1451 d_query_stmt
->nextRow(row
);
1452 } catch(SSqlException
&e
) {
1453 throw PDNSException("GSQLBackend comment get: "+e
.txtReason());
1455 // domain_id,name,type,modified_at,account,comment
1456 comment
.domain_id
= atol(row
[0].c_str());
1457 comment
.qname
= row
[1];
1458 comment
.qtype
= row
[2];
1459 comment
.modified_at
= atol(row
[3].c_str());
1460 comment
.account
= row
[4];
1461 comment
.content
= row
[5];
1466 void GSQLBackend::feedComment(const Comment
& comment
)
1469 d_InsertCommentQuery_stmt
->
1470 bind("domain_id",comment
.domain_id
)->
1471 bind("qname",toLower(comment
.qname
))->
1472 bind("qtype",comment
.qtype
.getName())->
1473 bind("modified_at",comment
.modified_at
)->
1474 bind("account",comment
.account
)->
1475 bind("content",comment
.content
)->
1479 catch (SSqlException
&e
) {
1480 throw PDNSException("GSQLBackend unable to feed comment: "+e
.txtReason());
1484 bool GSQLBackend::replaceComments(const uint32_t domain_id
, const string
& qname
, const QType
& qt
, const vector
<Comment
>& comments
)
1487 d_DeleteCommentRRsetQuery_stmt
->
1488 bind("domain_id",domain_id
)->
1489 bind("qname",toLower(qname
))->
1490 bind("qtype",qt
.getName())->
1494 catch (SSqlException
&e
) {
1495 throw PDNSException("GSQLBackend unable to delete comment: "+e
.txtReason());
1498 BOOST_FOREACH(const Comment
& comment
, comments
) {
1499 feedComment(comment
);
1505 SSqlStatement::~SSqlStatement() {
1506 // make sure vtable won't break