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.
25 #include "pdns/dns.hh"
26 #include "pdns/dnsbackend.hh"
27 #include "gsqlbackend.hh"
28 #include "pdns/dnspacket.hh"
29 #include "pdns/pdnsexception.hh"
30 #include "pdns/logger.hh"
31 #include "pdns/arguments.hh"
32 #include "pdns/base32.hh"
33 #include "pdns/dnssecinfra.hh"
34 #include <boost/algorithm/string.hpp>
36 #include <boost/format.hpp>
37 #include <boost/scoped_ptr.hpp>
39 #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())); } }
41 GSQLBackend::GSQLBackend(const string
&mode
, const string
&suffix
)
43 setArgPrefix(mode
+suffix
);
45 d_logprefix
="["+mode
+"Backend"+suffix
+"] ";
49 d_dnssecQueries
= mustDo("dnssec");
51 catch (const ArgException
&)
53 d_dnssecQueries
= false;
56 d_NoIdQuery
=getArg("basic-query");
57 d_IdQuery
=getArg("id-query");
58 d_ANYNoIdQuery
=getArg("any-query");
59 d_ANYIdQuery
=getArg("any-id-query");
61 d_listQuery
=getArg("list-query");
62 d_listSubZoneQuery
=getArg("list-subzone-query");
64 d_InfoOfDomainsZoneQuery
=getArg("info-zone-query");
65 d_InfoOfAllSlaveDomainsQuery
=getArg("info-all-slaves-query");
66 d_SuperMasterInfoQuery
=getArg("supermaster-query");
67 d_GetSuperMasterIPs
=getArg("supermaster-name-to-ips");
68 d_InsertZoneQuery
=getArg("insert-zone-query");
69 d_InsertRecordQuery
=getArg("insert-record-query");
70 d_UpdateMasterOfZoneQuery
=getArg("update-master-query");
71 d_UpdateKindOfZoneQuery
=getArg("update-kind-query");
72 d_UpdateSerialOfZoneQuery
=getArg("update-serial-query");
73 d_UpdateLastCheckofZoneQuery
=getArg("update-lastcheck-query");
74 d_UpdateAccountOfZoneQuery
=getArg("update-account-query");
75 d_InfoOfAllMasterDomainsQuery
=getArg("info-all-master-query");
76 d_DeleteDomainQuery
=getArg("delete-domain-query");
77 d_DeleteZoneQuery
=getArg("delete-zone-query");
78 d_DeleteRRSetQuery
=getArg("delete-rrset-query");
79 d_DeleteNamesQuery
=getArg("delete-names-query");
80 d_getAllDomainsQuery
=getArg("get-all-domains-query");
82 d_InsertEmptyNonTerminalOrderQuery
=getArg("insert-empty-non-terminal-order-query");
83 d_DeleteEmptyNonTerminalQuery
= getArg("delete-empty-non-terminal-query");
84 d_RemoveEmptyNonTerminalsFromZoneQuery
= getArg("remove-empty-non-terminals-from-zone-query");
86 d_ListCommentsQuery
= getArg("list-comments-query");
87 d_InsertCommentQuery
= getArg("insert-comment-query");
88 d_DeleteCommentRRsetQuery
= getArg("delete-comment-rrset-query");
89 d_DeleteCommentsQuery
= getArg("delete-comments-query");
91 d_firstOrderQuery
= getArg("get-order-first-query");
92 d_beforeOrderQuery
= getArg("get-order-before-query");
93 d_afterOrderQuery
= getArg("get-order-after-query");
94 d_lastOrderQuery
= getArg("get-order-last-query");
96 d_updateOrderNameAndAuthQuery
= getArg("update-ordername-and-auth-query");
97 d_updateOrderNameAndAuthTypeQuery
= getArg("update-ordername-and-auth-type-query");
98 d_nullifyOrderNameAndUpdateAuthQuery
= getArg("nullify-ordername-and-update-auth-query");
99 d_nullifyOrderNameAndUpdateAuthTypeQuery
= getArg("nullify-ordername-and-update-auth-type-query");
101 d_AddDomainKeyQuery
= getArg("add-domain-key-query");
102 d_GetLastInsertedKeyIdQuery
= getArg("get-last-inserted-key-id-query");
103 d_ListDomainKeysQuery
= getArg("list-domain-keys-query");
105 d_GetAllDomainMetadataQuery
= getArg("get-all-domain-metadata-query");
106 d_GetDomainMetadataQuery
= getArg("get-domain-metadata-query");
107 d_ClearDomainMetadataQuery
= getArg("clear-domain-metadata-query");
108 d_ClearDomainAllMetadataQuery
= getArg("clear-domain-all-metadata-query");
109 d_SetDomainMetadataQuery
= getArg("set-domain-metadata-query");
111 d_ActivateDomainKeyQuery
= getArg("activate-domain-key-query");
112 d_DeactivateDomainKeyQuery
= getArg("deactivate-domain-key-query");
113 d_RemoveDomainKeyQuery
= getArg("remove-domain-key-query");
114 d_ClearDomainAllKeysQuery
= getArg("clear-domain-all-keys-query");
116 d_getTSIGKeyQuery
= getArg("get-tsig-key-query");
117 d_setTSIGKeyQuery
= getArg("set-tsig-key-query");
118 d_deleteTSIGKeyQuery
= getArg("delete-tsig-key-query");
119 d_getTSIGKeysQuery
= getArg("get-tsig-keys-query");
121 d_SearchRecordsQuery
= getArg("search-records-query");
122 d_SearchCommentsQuery
= getArg("search-comments-query");
125 d_NoIdQuery_stmt
= NULL
;
126 d_IdQuery_stmt
= NULL
;
127 d_ANYNoIdQuery_stmt
= NULL
;
128 d_ANYIdQuery_stmt
= NULL
;
129 d_listQuery_stmt
= NULL
;
130 d_listSubZoneQuery_stmt
= NULL
;
131 d_InfoOfDomainsZoneQuery_stmt
= NULL
;
132 d_InfoOfAllSlaveDomainsQuery_stmt
= NULL
;
133 d_SuperMasterInfoQuery_stmt
= NULL
;
134 d_GetSuperMasterIPs_stmt
= NULL
;
135 d_InsertZoneQuery_stmt
= NULL
;
136 d_InsertRecordQuery_stmt
= NULL
;
137 d_InsertEmptyNonTerminalOrderQuery_stmt
= NULL
;
138 d_UpdateMasterOfZoneQuery_stmt
= NULL
;
139 d_UpdateKindOfZoneQuery_stmt
= NULL
;
140 d_UpdateSerialOfZoneQuery_stmt
= NULL
;
141 d_UpdateLastCheckofZoneQuery_stmt
= NULL
;
142 d_UpdateAccountOfZoneQuery_stmt
= NULL
;
143 d_InfoOfAllMasterDomainsQuery_stmt
= NULL
;
144 d_DeleteDomainQuery_stmt
= NULL
;
145 d_DeleteZoneQuery_stmt
= NULL
;
146 d_DeleteRRSetQuery_stmt
= NULL
;
147 d_DeleteNamesQuery_stmt
= NULL
;
148 d_firstOrderQuery_stmt
= NULL
;
149 d_beforeOrderQuery_stmt
= NULL
;
150 d_afterOrderQuery_stmt
= NULL
;
151 d_lastOrderQuery_stmt
= NULL
;
152 d_updateOrderNameAndAuthQuery_stmt
= NULL
;
153 d_updateOrderNameAndAuthTypeQuery_stmt
= NULL
;
154 d_nullifyOrderNameAndUpdateAuthQuery_stmt
= NULL
;
155 d_nullifyOrderNameAndUpdateAuthTypeQuery_stmt
= NULL
;
156 d_RemoveEmptyNonTerminalsFromZoneQuery_stmt
= NULL
;
157 d_DeleteEmptyNonTerminalQuery_stmt
= NULL
;
158 d_AddDomainKeyQuery_stmt
= NULL
;
159 d_GetLastInsertedKeyIdQuery_stmt
= NULL
;
160 d_ListDomainKeysQuery_stmt
= NULL
;
161 d_GetAllDomainMetadataQuery_stmt
= NULL
;
162 d_GetDomainMetadataQuery_stmt
= NULL
;
163 d_ClearDomainMetadataQuery_stmt
= NULL
;
164 d_ClearDomainAllMetadataQuery_stmt
= NULL
;
165 d_SetDomainMetadataQuery_stmt
= NULL
;
166 d_RemoveDomainKeyQuery_stmt
= NULL
;
167 d_ActivateDomainKeyQuery_stmt
= NULL
;
168 d_DeactivateDomainKeyQuery_stmt
= NULL
;
169 d_ClearDomainAllKeysQuery_stmt
= NULL
;
170 d_getTSIGKeyQuery_stmt
= NULL
;
171 d_setTSIGKeyQuery_stmt
= NULL
;
172 d_deleteTSIGKeyQuery_stmt
= NULL
;
173 d_getTSIGKeysQuery_stmt
= NULL
;
174 d_getAllDomainsQuery_stmt
= NULL
;
175 d_ListCommentsQuery_stmt
= NULL
;
176 d_InsertCommentQuery_stmt
= NULL
;
177 d_DeleteCommentRRsetQuery_stmt
= NULL
;
178 d_DeleteCommentsQuery_stmt
= NULL
;
179 d_SearchRecordsQuery_stmt
= NULL
;
180 d_SearchCommentsQuery_stmt
= NULL
;
183 void GSQLBackend::setNotified(uint32_t domain_id
, uint32_t serial
)
188 d_UpdateSerialOfZoneQuery_stmt
->
189 bind("serial", serial
)->
190 bind("domain_id", domain_id
)->
194 catch(SSqlException
&e
) {
195 throw PDNSException("GSQLBackend unable to refresh domain_id "+itoa(domain_id
)+": "+e
.txtReason());
199 void GSQLBackend::setFresh(uint32_t domain_id
)
204 d_UpdateLastCheckofZoneQuery_stmt
->
205 bind("last_check", time(0))->
206 bind("domain_id", domain_id
)->
210 catch (SSqlException
&e
) {
211 throw PDNSException("GSQLBackend unable to refresh domain_id "+itoa(domain_id
)+": "+e
.txtReason());
215 bool GSQLBackend::setMaster(const DNSName
&domain
, const string
&ip
)
220 d_UpdateMasterOfZoneQuery_stmt
->
222 bind("domain", domain
)->
226 catch (SSqlException
&e
) {
227 throw PDNSException("GSQLBackend unable to set master of domain '"+domain
.toLogString()+"' to IP address " + ip
+ ": "+e
.txtReason());
232 bool GSQLBackend::setKind(const DNSName
&domain
, const DomainInfo::DomainKind kind
)
237 d_UpdateKindOfZoneQuery_stmt
->
238 bind("kind", toUpper(DomainInfo::getKindString(kind
)))->
239 bind("domain", domain
)->
243 catch (SSqlException
&e
) {
244 throw PDNSException("GSQLBackend unable to set kind of domain '"+domain
.toLogString()+"' to " + toUpper(DomainInfo::getKindString(kind
)) + ": "+e
.txtReason());
249 bool GSQLBackend::setAccount(const DNSName
&domain
, const string
&account
)
254 d_UpdateAccountOfZoneQuery_stmt
->
255 bind("account", account
)->
256 bind("domain", domain
)->
260 catch (SSqlException
&e
) {
261 throw PDNSException("GSQLBackend unable to set account of domain '"+domain
.toLogString()+"' to '" + account
+ "': "+e
.txtReason());
266 bool GSQLBackend::getDomainInfo(const DNSName
&domain
, DomainInfo
&di
, bool getSerial
)
268 /* fill DomainInfo from database info:
269 id,name,master IP(s),last_check,notified_serial,type,account */
273 d_InfoOfDomainsZoneQuery_stmt
->
274 bind("domain", domain
)->
276 getResult(d_result
)->
279 catch(SSqlException
&e
) {
280 throw PDNSException("GSQLBackend unable to retrieve information about domain '" + domain
.toLogString() + "': "+e
.txtReason());
283 int numanswers
=d_result
.size();
287 ASSERT_ROW_COLUMNS("info-zone-query", d_result
[0], 7);
289 di
.id
=pdns_stou(d_result
[0][0]);
291 di
.zone
=DNSName(d_result
[0][1]);
295 string type
=d_result
[0][5];
296 di
.account
=d_result
[0][6];
297 di
.kind
= DomainInfo::stringToKind(type
);
299 vector
<string
> masters
;
300 stringtok(masters
, d_result
[0][2], " ,\t");
301 for(const auto& m
: masters
)
302 di
.masters
.emplace_back(m
, 53);
303 di
.last_check
=pdns_stou(d_result
[0][3]);
304 di
.notified_serial
= pdns_stou(d_result
[0][4]);
311 if(!getSOA(domain
, sd
))
312 g_log
<<Logger::Notice
<<"No serial for '"<<domain
<<"' found - zone is missing?"<<endl
;
314 di
.serial
= sd
.serial
;
316 catch(PDNSException
&ae
){
317 g_log
<<Logger::Error
<<"Error retrieving serial for '"<<domain
<<"': "<<ae
.reason
<<endl
;
324 void GSQLBackend::getUnfreshSlaveInfos(vector
<DomainInfo
> *unfreshDomains
)
326 /* list all domains that need refreshing for which we are slave, and insert into SlaveDomain:
327 id,name,master IP,serial */
331 d_InfoOfAllSlaveDomainsQuery_stmt
->
333 getResult(d_result
)->
336 catch (SSqlException
&e
) {
337 throw PDNSException("GSQLBackend unable to retrieve list of slave domains: "+e
.txtReason());
340 vector
<DomainInfo
> allSlaves
;
342 bool loggedAssertRowColumns
= false;
343 for(const auto& row
: d_result
) { // id,name,master,last_check
346 ASSERT_ROW_COLUMNS("info-all-slaves-query", row
, 4);
347 } catch(const PDNSException
&e
) {
348 if (!loggedAssertRowColumns
) {
349 g_log
<<Logger::Warning
<<e
.reason
<<endl
;
351 loggedAssertRowColumns
= true;
356 sd
.zone
= DNSName(row
[1]);
357 } catch(const std::runtime_error
&e
) {
358 g_log
<<Logger::Warning
<<"Domain name '"<<row
[1]<<"' is not a valid DNS name: "<<e
.what()<<endl
;
363 sd
.id
=pdns_stou(row
[0]);
364 } catch (const std::exception
&e
) {
365 g_log
<<Logger::Warning
<<"Could not convert id ("<<row
[0]<<") for domain '"<<sd
.zone
<<"' into an integer: "<<e
.what()<<endl
;
369 vector
<string
> masters
;
370 stringtok(masters
, row
[2], ", \t");
371 for(const auto& m
: masters
) {
373 sd
.masters
.emplace_back(m
, 53);
374 } catch(const PDNSException
&e
) {
375 g_log
<<Logger::Warning
<<"Could not parse master address ("<<m
<<") for zone '"<<sd
.zone
<<"': "<<e
.reason
<<endl
;
378 if (sd
.masters
.empty()) {
379 g_log
<<Logger::Warning
<<"No masters for slave zone '"<<sd
.zone
<<"' found in the database"<<endl
;
384 sd
.last_check
=pdns_stou(row
[3]);
385 } catch (const std::exception
&e
) {
386 g_log
<<Logger::Warning
<<"Could not convert last_check ("<<row
[3]<<") for domain '"<<sd
.zone
<<"' into an integer: "<<e
.what()<<endl
;
391 sd
.kind
=DomainInfo::Slave
;
392 allSlaves
.push_back(sd
);
395 for (auto& slave
: allSlaves
) {
400 getSOA(slave
.zone
, sdata
);
401 if(static_cast<time_t>(slave
.last_check
+ sdata
.refresh
) < time(nullptr)) {
402 slave
.serial
=sdata
.serial
;
403 unfreshDomains
->push_back(slave
);
406 catch(const std::exception
& exp
) {
407 g_log
<<Logger::Warning
<<"Error while parsing SOA data for slave zone '"<<slave
.zone
.toLogString()<<"': "<<exp
.what()<<endl
;
411 g_log
<<Logger::Warning
<<"Error while parsing SOA data for slave zone '"<<slave
.zone
.toLogString()<<"', skipping"<<endl
;
417 void GSQLBackend::getUpdatedMasters(vector
<DomainInfo
> *updatedDomains
)
419 /* list all domains that need notifications for which we are master, and insert into updatedDomains
420 id, name, notified_serial, serial */
424 d_InfoOfAllMasterDomainsQuery_stmt
->
426 getResult(d_result
)->
429 catch(SSqlException
&e
) {
430 throw PDNSException("GSQLBackend unable to retrieve list of master domains: "+e
.txtReason());
433 size_t numanswers
=d_result
.size();
438 di
.kind
= DomainInfo::Master
;
440 for( size_t n
= 0; n
< numanswers
; ++n
) { // id, name, notified_serial, content
441 ASSERT_ROW_COLUMNS( "info-all-master-query", d_result
[n
], 4 );
444 stringtok( parts
, d_result
[n
][3] );
447 uint32_t serial
= parts
.size() > 2 ? pdns_stou(parts
[2]) : 0;
448 uint32_t notified_serial
= pdns_stou( d_result
[n
][2] );
450 if( serial
!= notified_serial
) {
451 di
.id
= pdns_stou( d_result
[n
][0] );
452 di
.zone
= DNSName( d_result
[n
][1] );
454 di
.notified_serial
= notified_serial
;
456 updatedDomains
->emplace_back(di
);
464 bool GSQLBackend::updateDNSSECOrderNameAndAuth(uint32_t domain_id
, const DNSName
& qname
, const DNSName
& ordername
, bool auth
, const uint16_t qtype
)
469 if (!ordername
.empty()) {
470 if (qtype
== QType::ANY
) {
474 d_updateOrderNameAndAuthQuery_stmt
->
475 bind("ordername", ordername
.labelReverse().toString(" ", false))->
477 bind("domain_id", domain_id
)->
478 bind("qname", qname
)->
482 catch(SSqlException
&e
) {
483 throw PDNSException("GSQLBackend unable to update ordername and auth for " + qname
.toLogString() + " for domain_id "+itoa(domain_id
)+", domain name '" + qname
.toLogString() + "': "+e
.txtReason());
489 d_updateOrderNameAndAuthTypeQuery_stmt
->
490 bind("ordername", ordername
.labelReverse().toString(" ", false))->
492 bind("domain_id", domain_id
)->
493 bind("qname", qname
)->
494 bind("qtype", QType(qtype
).getName())->
498 catch(SSqlException
&e
) {
499 throw PDNSException("GSQLBackend unable to update ordername and auth for " + qname
.toLogString() + "|" + QType(qtype
).getName() + " for domain_id "+itoa(domain_id
)+": "+e
.txtReason());
503 if (qtype
== QType::ANY
) {
507 d_nullifyOrderNameAndUpdateAuthQuery_stmt
->
509 bind("domain_id", domain_id
)->
510 bind("qname", qname
)->
514 catch(SSqlException
&e
) {
515 throw PDNSException("GSQLBackend unable to nullify ordername and update auth for " + qname
.toLogString() + " for domain_id "+itoa(domain_id
)+": "+e
.txtReason());
521 d_nullifyOrderNameAndUpdateAuthTypeQuery_stmt
->
523 bind("domain_id", domain_id
)->
524 bind("qname", qname
)->
525 bind("qtype", QType(qtype
).getName())->
529 catch(SSqlException
&e
) {
530 throw PDNSException("GSQLBackend unable to nullify ordername and update auth for " + qname
.toLogString() + "|" + QType(qtype
).getName() + " for domain_id "+itoa(domain_id
)+": "+e
.txtReason());
537 bool GSQLBackend::updateEmptyNonTerminals(uint32_t domain_id
, set
<DNSName
>& insert
, set
<DNSName
>& erase
, bool remove
)
543 d_RemoveEmptyNonTerminalsFromZoneQuery_stmt
->
544 bind("domain_id", domain_id
)->
548 catch (SSqlException
&e
) {
549 throw PDNSException("GSQLBackend unable to delete empty non-terminal records from domain_id "+itoa(domain_id
)+": "+e
.txtReason());
555 for(const auto& qname
: erase
) {
559 d_DeleteEmptyNonTerminalQuery_stmt
->
560 bind("domain_id", domain_id
)->
561 bind("qname", qname
)->
565 catch (SSqlException
&e
) {
566 throw PDNSException("GSQLBackend unable to delete empty non-terminal rr '"+qname
.toLogString()+"' from domain_id "+itoa(domain_id
)+": "+e
.txtReason());
572 for(const auto& qname
: insert
) {
576 d_InsertEmptyNonTerminalOrderQuery_stmt
->
577 bind("domain_id", domain_id
)->
578 bind("qname", qname
)->
579 bindNull("ordername")->
584 catch (SSqlException
&e
) {
585 throw PDNSException("GSQLBackend unable to insert empty non-terminal rr '"+qname
.toLogString()+"' in domain_id "+itoa(domain_id
)+": "+e
.txtReason());
593 bool GSQLBackend::doesDNSSEC()
595 return d_dnssecQueries
;
598 bool GSQLBackend::getBeforeAndAfterNamesAbsolute(uint32_t id
, const DNSName
& qname
, DNSName
& unhashed
, DNSName
& before
, DNSName
& after
)
604 SSqlStatement::row_t row
;
608 d_afterOrderQuery_stmt
->
609 bind("ordername", qname
.labelReverse().toString(" ", false))->
610 bind("domain_id", id
)->
612 while(d_afterOrderQuery_stmt
->hasNextRow()) {
613 d_afterOrderQuery_stmt
->nextRow(row
);
614 ASSERT_ROW_COLUMNS("get-order-after-query", row
, 1);
615 if(! row
[0].empty()) { // Hack because NULL values are passed on as empty strings
616 after
=DNSName(boost::replace_all_copy(row
[0]," ",".")).labelReverse();
619 d_afterOrderQuery_stmt
->reset();
621 catch(SSqlException
&e
) {
622 throw PDNSException("GSQLBackend unable to find before/after (after) for domain_id "+itoa(id
)+" and qname '"+ qname
.toLogString() +"': "+e
.txtReason());
629 d_firstOrderQuery_stmt
->
630 bind("domain_id", id
)->
632 while(d_firstOrderQuery_stmt
->hasNextRow()) {
633 d_firstOrderQuery_stmt
->nextRow(row
);
634 ASSERT_ROW_COLUMNS("get-order-first-query", row
, 1);
635 after
=DNSName(boost::replace_all_copy(row
[0]," ",".")).labelReverse();
637 d_firstOrderQuery_stmt
->reset();
639 catch(SSqlException
&e
) {
640 throw PDNSException("GSQLBackend unable to find before/after (first) for domain_id "+itoa(id
)+" and qname '"+ qname
.toLogString() + "': "+e
.txtReason());
644 if (before
.empty()) {
650 d_beforeOrderQuery_stmt
->
651 bind("ordername", qname
.labelReverse().toString(" ", false))->
652 bind("domain_id", id
)->
654 while(d_beforeOrderQuery_stmt
->hasNextRow()) {
655 d_beforeOrderQuery_stmt
->nextRow(row
);
656 ASSERT_ROW_COLUMNS("get-order-before-query", row
, 2);
657 before
=DNSName(boost::replace_all_copy(row
[0]," ",".")).labelReverse();
659 unhashed
=DNSName(row
[1]);
664 d_beforeOrderQuery_stmt
->reset();
666 catch(SSqlException
&e
) {
667 throw PDNSException("GSQLBackend unable to find before/after (before) for domain_id "+itoa(id
)+" and qname '"+ qname
.toLogString() + ": "+e
.txtReason());
670 if(! unhashed
.empty())
672 // cerr<<"unhashed="<<unhashed<<",before="<<before<<", after="<<after<<endl;
679 d_lastOrderQuery_stmt
->
680 bind("domain_id", id
)->
682 while(d_lastOrderQuery_stmt
->hasNextRow()) {
683 d_lastOrderQuery_stmt
->nextRow(row
);
684 ASSERT_ROW_COLUMNS("get-order-last-query", row
, 2);
685 before
=DNSName(boost::replace_all_copy(row
[0]," ",".")).labelReverse();
687 unhashed
=DNSName(row
[1]);
692 d_lastOrderQuery_stmt
->reset();
694 catch(SSqlException
&e
) {
695 throw PDNSException("GSQLBackend unable to find before/after (last) for domain_id "+itoa(id
)+" and qname '"+ qname
.toLogString() + ": "+e
.txtReason());
704 bool GSQLBackend::addDomainKey(const DNSName
& name
, const KeyData
& key
, int64_t& id
)
712 d_AddDomainKeyQuery_stmt
->
713 bind("flags", key
.flags
)->
714 bind("active", key
.active
)->
715 bind("content", key
.content
)->
716 bind("domain", name
)->
720 catch (SSqlException
&e
) {
721 throw PDNSException("GSQLBackend unable to store key for domain '"+ name
.toLogString() + "': "+e
.txtReason());
727 d_GetLastInsertedKeyIdQuery_stmt
->execute();
728 if (!d_GetLastInsertedKeyIdQuery_stmt
->hasNextRow()) {
732 SSqlStatement::row_t row
;
733 d_GetLastInsertedKeyIdQuery_stmt
->nextRow(row
);
734 ASSERT_ROW_COLUMNS("get-last-inserted-key-id-query", row
, 1);
735 id
= std::stoi(row
[0]);
736 d_GetLastInsertedKeyIdQuery_stmt
->reset();
739 catch (SSqlException
&e
) {
747 bool GSQLBackend::activateDomainKey(const DNSName
& name
, unsigned int id
)
755 d_ActivateDomainKeyQuery_stmt
->
756 bind("domain", name
)->
761 catch (SSqlException
&e
) {
762 throw PDNSException("GSQLBackend unable to activate key with id "+ std::to_string(id
) + " for domain '" + name
.toLogString() + "': "+e
.txtReason());
767 bool GSQLBackend::deactivateDomainKey(const DNSName
& name
, unsigned int id
)
775 d_DeactivateDomainKeyQuery_stmt
->
776 bind("domain", name
)->
781 catch (SSqlException
&e
) {
782 throw PDNSException("GSQLBackend unable to deactivate key with id "+ std::to_string(id
) + " for domain '" + name
.toLogString() + "': "+e
.txtReason());
787 bool GSQLBackend::removeDomainKey(const DNSName
& name
, unsigned int id
)
795 d_RemoveDomainKeyQuery_stmt
->
796 bind("domain", name
)->
801 catch (SSqlException
&e
) {
802 throw PDNSException("GSQLBackend unable to remove key with id "+ std::to_string(id
) + " for domain '" + name
.toLogString() + "': "+e
.txtReason());
807 bool GSQLBackend::getTSIGKey(const DNSName
& name
, DNSName
* algorithm
, string
* content
)
812 d_getTSIGKeyQuery_stmt
->
813 bind("key_name", name
)->
816 SSqlStatement::row_t row
;
819 while(d_getTSIGKeyQuery_stmt
->hasNextRow()) {
820 d_getTSIGKeyQuery_stmt
->nextRow(row
);
821 ASSERT_ROW_COLUMNS("get-tsig-key-query", row
, 2);
823 if(algorithm
->empty() || *algorithm
==DNSName(row
[0])) {
824 *algorithm
= DNSName(row
[0]);
830 d_getTSIGKeyQuery_stmt
->reset();
832 catch (SSqlException
&e
) {
833 throw PDNSException("GSQLBackend unable to retrieve TSIG key with name '" + name
.toLogString() + "': "+e
.txtReason());
836 return !content
->empty();
839 bool GSQLBackend::setTSIGKey(const DNSName
& name
, const DNSName
& algorithm
, const string
& content
)
844 d_setTSIGKeyQuery_stmt
->
845 bind("key_name", name
)->
846 bind("algorithm", algorithm
)->
847 bind("content", content
)->
851 catch (SSqlException
&e
) {
852 throw PDNSException("GSQLBackend unable to store TSIG key with name '" + name
.toLogString() + "' and algorithm '" + algorithm
.toString() + "': "+e
.txtReason());
857 bool GSQLBackend::deleteTSIGKey(const DNSName
& name
)
862 d_deleteTSIGKeyQuery_stmt
->
863 bind("key_name", name
)->
867 catch (SSqlException
&e
) {
868 throw PDNSException("GSQLBackend unable to delete TSIG key with name '" + name
.toLogString() + "': "+e
.txtReason());
873 bool GSQLBackend::getTSIGKeys(std::vector
< struct TSIGKey
> &keys
)
878 d_getTSIGKeysQuery_stmt
->
881 SSqlStatement::row_t row
;
883 while(d_getTSIGKeysQuery_stmt
->hasNextRow()) {
884 d_getTSIGKeysQuery_stmt
->nextRow(row
);
885 ASSERT_ROW_COLUMNS("get-tsig-keys-query", row
, 3);
888 key
.name
= DNSName(row
[0]);
889 key
.algorithm
= DNSName(row
[1]);
897 d_getTSIGKeysQuery_stmt
->reset();
899 catch (SSqlException
&e
) {
900 throw PDNSException("GSQLBackend unable to retrieve TSIG keys: "+e
.txtReason());
906 bool GSQLBackend::getDomainKeys(const DNSName
& name
, std::vector
<KeyData
>& keys
)
914 d_ListDomainKeysQuery_stmt
->
915 bind("domain", name
)->
918 SSqlStatement::row_t row
;
920 while(d_ListDomainKeysQuery_stmt
->hasNextRow()) {
921 d_ListDomainKeysQuery_stmt
->nextRow(row
);
922 ASSERT_ROW_COLUMNS("list-domain-keys-query", row
, 4);
923 //~ for(const auto& val: row) {
924 //~ cerr<<"'"<<val<<"'"<<endl;
926 kd
.id
= pdns_stou(row
[0]);
927 kd
.flags
= pdns_stou(row
[1]);
928 kd
.active
= row
[2] == "1";
933 d_ListDomainKeysQuery_stmt
->reset();
935 catch (SSqlException
&e
) {
936 throw PDNSException("GSQLBackend unable to list keys: "+e
.txtReason());
942 void GSQLBackend::alsoNotifies(const DNSName
&domain
, set
<string
> *ips
)
945 getDomainMetadata(domain
, "ALSO-NOTIFY", meta
);
946 for(const auto& str
: meta
) {
951 bool GSQLBackend::getAllDomainMetadata(const DNSName
& name
, std::map
<std::string
, std::vector
<std::string
> >& meta
)
956 d_GetAllDomainMetadataQuery_stmt
->
957 bind("domain", name
)->
960 SSqlStatement::row_t row
;
962 while(d_GetAllDomainMetadataQuery_stmt
->hasNextRow()) {
963 d_GetAllDomainMetadataQuery_stmt
->nextRow(row
);
964 ASSERT_ROW_COLUMNS("get-all-domain-metadata-query", row
, 2);
966 if (d_dnssecQueries
|| !isDnssecDomainMetadata(row
[0]))
967 meta
[row
[0]].push_back(row
[1]);
970 d_GetAllDomainMetadataQuery_stmt
->reset();
972 catch (SSqlException
&e
) {
973 throw PDNSException("GSQLBackend unable to list metadata for domain '" + name
.toLogString() + "': "+e
.txtReason());
980 bool GSQLBackend::getDomainMetadata(const DNSName
& name
, const std::string
& kind
, std::vector
<std::string
>& meta
)
982 if(!d_dnssecQueries
&& isDnssecDomainMetadata(kind
))
988 d_GetDomainMetadataQuery_stmt
->
989 bind("domain", name
)->
993 SSqlStatement::row_t row
;
995 while(d_GetDomainMetadataQuery_stmt
->hasNextRow()) {
996 d_GetDomainMetadataQuery_stmt
->nextRow(row
);
997 ASSERT_ROW_COLUMNS("get-domain-metadata-query", row
, 1);
998 meta
.push_back(row
[0]);
1001 d_GetDomainMetadataQuery_stmt
->reset();
1003 catch (SSqlException
&e
) {
1004 throw PDNSException("GSQLBackend unable to get metadata kind '" + kind
+ "' for domain '" + name
.toLogString() + "': "+e
.txtReason());
1010 bool GSQLBackend::setDomainMetadata(const DNSName
& name
, const std::string
& kind
, const std::vector
<std::string
>& meta
)
1012 if(!d_dnssecQueries
&& isDnssecDomainMetadata(kind
))
1016 reconnectIfNeeded();
1018 d_ClearDomainMetadataQuery_stmt
->
1019 bind("domain", name
)->
1020 bind("kind", kind
)->
1024 for(const auto& value
: meta
) {
1025 d_SetDomainMetadataQuery_stmt
->
1026 bind("kind", kind
)->
1027 bind("content", value
)->
1028 bind("domain", name
)->
1034 catch (SSqlException
&e
) {
1035 throw PDNSException("GSQLBackend unable to set metadata kind '" + kind
+ "' for domain '" + name
.toLogString() + "': "+e
.txtReason());
1041 void GSQLBackend::lookup(const QType
&qtype
,const DNSName
&qname
, DNSPacket
*pkt_p
, int domain_id
)
1044 reconnectIfNeeded();
1046 if(qtype
.getCode()!=QType::ANY
) {
1048 d_query_name
= "basic-query";
1049 d_query_stmt
= &d_NoIdQuery_stmt
;
1051 bind("qtype", qtype
.getName())->
1052 bind("qname", qname
);
1054 d_query_name
= "id-query";
1055 d_query_stmt
= &d_IdQuery_stmt
;
1057 bind("qtype", qtype
.getName())->
1058 bind("qname", qname
)->
1059 bind("domain_id", domain_id
);
1064 d_query_name
= "any-query";
1065 d_query_stmt
= &d_ANYNoIdQuery_stmt
;
1067 bind("qname", qname
);
1069 d_query_name
= "any-id-query";
1070 d_query_stmt
= &d_ANYIdQuery_stmt
;
1072 bind("qname", qname
)->
1073 bind("domain_id", domain_id
);
1080 catch(SSqlException
&e
) {
1081 throw PDNSException("GSQLBackend unable to lookup '" + qname
.toLogString() + "|" + qtype
.getName() + "':"+e
.txtReason());
1087 bool GSQLBackend::list(const DNSName
&target
, int domain_id
, bool include_disabled
)
1089 DLOG(g_log
<<"GSQLBackend constructing handle for list of domain id '"<<domain_id
<<"'"<<endl
);
1092 reconnectIfNeeded();
1094 d_query_name
= "list-query";
1095 d_query_stmt
= &d_listQuery_stmt
;
1097 bind("include_disabled", (int)include_disabled
)->
1098 bind("domain_id", domain_id
)->
1101 catch(SSqlException
&e
) {
1102 throw PDNSException("GSQLBackend unable to list domain '" + target
.toLogString() + "': "+e
.txtReason());
1109 bool GSQLBackend::listSubZone(const DNSName
&zone
, int domain_id
) {
1111 string wildzone
= "%." + zone
.makeLowerCase().toStringNoDot();
1114 reconnectIfNeeded();
1116 d_query_name
= "list-subzone-query";
1117 d_query_stmt
= &d_listSubZoneQuery_stmt
;
1119 bind("zone", zone
)->
1120 bind("wildzone", wildzone
)->
1121 bind("domain_id", domain_id
)->
1124 catch(SSqlException
&e
) {
1125 throw PDNSException("GSQLBackend unable to list SubZones for domain '" + zone
.toLogString() + "': "+e
.txtReason());
1131 bool GSQLBackend::get(DNSResourceRecord
&r
)
1133 // g_log << "GSQLBackend get() was called for "<<qtype.getName() << " record: ";
1134 SSqlStatement::row_t row
;
1137 if((*d_query_stmt
)->hasNextRow()) {
1139 (*d_query_stmt
)->nextRow(row
);
1140 ASSERT_ROW_COLUMNS(d_query_name
, row
, 8);
1141 } catch (SSqlException
&e
) {
1142 throw PDNSException("GSQLBackend get: "+e
.txtReason());
1145 extractRecord(row
, r
);
1153 (*d_query_stmt
)->reset();
1154 } catch (SSqlException
&e
) {
1155 throw PDNSException("GSQLBackend get: "+e
.txtReason());
1157 d_query_stmt
= NULL
;
1161 bool GSQLBackend::superMasterBackend(const string
&ip
, const DNSName
&domain
, const vector
<DNSResourceRecord
>&nsset
, string
*nameserver
, string
*account
, DNSBackend
**ddb
)
1163 // check if we know the ip/ns couple in the database
1164 for(vector
<DNSResourceRecord
>::const_iterator i
=nsset
.begin();i
!=nsset
.end();++i
) {
1166 reconnectIfNeeded();
1168 d_SuperMasterInfoQuery_stmt
->
1170 bind("nameserver", i
->content
)->
1172 getResult(d_result
)->
1175 catch (SSqlException
&e
) {
1176 throw PDNSException("GSQLBackend unable to search for a supermaster with IP " + ip
+ " and nameserver name '" + i
->content
+ "' for domain '" + domain
.toLogString() + "': "+e
.txtReason());
1178 if(!d_result
.empty()) {
1179 ASSERT_ROW_COLUMNS("supermaster-query", d_result
[0], 1);
1180 *nameserver
=i
->content
;
1181 *account
=d_result
[0][0];
1189 bool GSQLBackend::createDomain(const DNSName
&domain
, const string
&type
, const string
&masters
, const string
&account
)
1192 reconnectIfNeeded();
1194 d_InsertZoneQuery_stmt
->
1195 bind("type", type
)->
1196 bind("domain", domain
)->
1197 bind("masters", masters
)->
1198 bind("account", account
)->
1202 catch(SSqlException
&e
) {
1203 throw PDNSException("Database error trying to insert new domain '"+domain
.toLogString()+"': "+ e
.txtReason());
1208 bool GSQLBackend::createSlaveDomain(const string
&ip
, const DNSName
&domain
, const string
&nameserver
, const string
&account
)
1213 if (!nameserver
.empty()) {
1214 // figure out all IP addresses for the master
1215 reconnectIfNeeded();
1217 d_GetSuperMasterIPs_stmt
->
1218 bind("nameserver", nameserver
)->
1219 bind("account", account
)->
1221 getResult(d_result
)->
1223 if (!d_result
.empty()) {
1224 // collect all IP addresses
1226 for(const auto& row
: d_result
) {
1227 if (account
== row
[1])
1228 tmp
.push_back(row
[0]);
1230 // set them as domain's masters, comma separated
1231 masters
= boost::join(tmp
, ", ");
1234 createDomain(domain
, "SLAVE", masters
, account
);
1236 catch(SSqlException
&e
) {
1237 throw PDNSException("Database error trying to insert new slave domain '"+domain
.toLogString()+"': "+ e
.txtReason());
1242 bool GSQLBackend::deleteDomain(const DNSName
&domain
)
1245 if (!getDomainInfo(domain
, di
)) {
1250 reconnectIfNeeded();
1252 d_DeleteZoneQuery_stmt
->
1253 bind("domain_id", di
.id
)->
1256 d_ClearDomainAllMetadataQuery_stmt
->
1257 bind("domain", domain
)->
1260 d_ClearDomainAllKeysQuery_stmt
->
1261 bind("domain", domain
)->
1264 d_DeleteCommentsQuery_stmt
->
1265 bind("domain_id", di
.id
)->
1268 d_DeleteDomainQuery_stmt
->
1269 bind("domain", domain
)->
1273 catch(SSqlException
&e
) {
1274 throw PDNSException("Database error trying to delete domain '"+domain
.toLogString()+"': "+ e
.txtReason());
1279 void GSQLBackend::getAllDomains(vector
<DomainInfo
> *domains
, bool include_disabled
)
1281 DLOG(g_log
<<"GSQLBackend retrieving all domains."<<endl
);
1284 reconnectIfNeeded();
1286 d_getAllDomainsQuery_stmt
->
1287 bind("include_disabled", (int)include_disabled
)->
1290 SSqlStatement::row_t row
;
1291 while (d_getAllDomainsQuery_stmt
->hasNextRow()) {
1292 d_getAllDomainsQuery_stmt
->nextRow(row
);
1293 ASSERT_ROW_COLUMNS("get-all-domains-query", row
, 8);
1295 di
.id
= pdns_stou(row
[0]);
1297 di
.zone
= DNSName(row
[1]);
1302 if (pdns_iequals(row
[3], "MASTER")) {
1303 di
.kind
= DomainInfo::Master
;
1304 } else if (pdns_iequals(row
[3], "SLAVE")) {
1305 di
.kind
= DomainInfo::Slave
;
1306 } else if (pdns_iequals(row
[3], "NATIVE")) {
1307 di
.kind
= DomainInfo::Native
;
1309 g_log
<<Logger::Warning
<<"Could not parse domain kind '"<<row
[3]<<"' as one of 'MASTER', 'SLAVE' or 'NATIVE'. Setting zone kind to 'NATIVE'"<<endl
;
1310 di
.kind
= DomainInfo::Native
;
1313 if (!row
[4].empty()) {
1314 vector
<string
> masters
;
1315 stringtok(masters
, row
[4], " ,\t");
1316 for(const auto& m
: masters
) {
1318 di
.masters
.emplace_back(m
, 53);
1319 } catch(const PDNSException
&e
) {
1320 g_log
<<Logger::Warning
<<"Could not parse master address ("<<m
<<") for zone '"<<di
.zone
<<"': "<<e
.reason
;
1326 fillSOAData(row
[2], sd
);
1327 di
.serial
= sd
.serial
;
1329 di
.notified_serial
= pdns_stou(row
[5]);
1330 di
.last_check
= pdns_stou(row
[6]);
1334 di
.account
= row
[7];
1338 domains
->push_back(di
);
1340 d_getAllDomainsQuery_stmt
->reset();
1342 catch (SSqlException
&e
) {
1343 throw PDNSException("Database error trying to retrieve all domains:" + e
.txtReason());
1347 bool GSQLBackend::replaceRRSet(uint32_t domain_id
, const DNSName
& qname
, const QType
& qt
, const vector
<DNSResourceRecord
>& rrset
)
1350 reconnectIfNeeded();
1352 if (qt
!= QType::ANY
) {
1353 d_DeleteRRSetQuery_stmt
->
1354 bind("domain_id", domain_id
)->
1355 bind("qname", qname
)->
1356 bind("qtype", qt
.getName())->
1360 d_DeleteNamesQuery_stmt
->
1361 bind("domain_id", domain_id
)->
1362 bind("qname", qname
)->
1367 catch (SSqlException
&e
) {
1368 throw PDNSException("GSQLBackend unable to delete RRSet " + qname
.toLogString() + "|" + qt
.getName() + ": "+e
.txtReason());
1371 if (rrset
.empty()) {
1373 reconnectIfNeeded();
1375 d_DeleteCommentRRsetQuery_stmt
->
1376 bind("domain_id", domain_id
)->
1377 bind("qname", qname
)->
1378 bind("qtype", qt
.getName())->
1382 catch (SSqlException
&e
) {
1383 throw PDNSException("GSQLBackend unable to delete comment for RRSet " + qname
.toLogString() + "|" + qt
.getName() + ": "+e
.txtReason());
1386 for(const auto& rr
: rrset
) {
1387 feedRecord(rr
, DNSName());
1393 bool GSQLBackend::feedRecord(const DNSResourceRecord
&r
, const DNSName
&ordername
, bool ordernameIsNSEC3
)
1396 string
content(r
.content
);
1397 if (r
.qtype
== QType::MX
|| r
.qtype
== QType::SRV
) {
1398 string::size_type pos
= content
.find_first_not_of("0123456789");
1399 if (pos
!= string::npos
) {
1400 prio
=pdns_stou(content
.substr(0,pos
));
1401 boost::erase_head(content
, pos
);
1407 reconnectIfNeeded();
1409 d_InsertRecordQuery_stmt
->
1410 bind("content",content
)->
1412 bind("priority",prio
)->
1413 bind("qtype",r
.qtype
.getName())->
1414 bind("domain_id",r
.domain_id
)->
1415 bind("disabled",r
.disabled
)->
1416 bind("qname",r
.qname
);
1418 if (!ordername
.empty())
1419 d_InsertRecordQuery_stmt
->bind("ordername", ordername
.labelReverse().makeLowerCase().toString(" ", false));
1421 d_InsertRecordQuery_stmt
->bindNull("ordername");
1423 if (d_dnssecQueries
)
1424 d_InsertRecordQuery_stmt
->bind("auth", r
.auth
);
1426 d_InsertRecordQuery_stmt
->bind("auth", true);
1428 d_InsertRecordQuery_stmt
->
1432 catch (SSqlException
&e
) {
1433 throw PDNSException("GSQLBackend unable to feed record " + r
.qname
.toLogString() + "|" + r
.qtype
.getName() + ": "+e
.txtReason());
1435 return true; // XXX FIXME this API should not return 'true' I think -ahu
1438 bool GSQLBackend::feedEnts(int domain_id
, map
<DNSName
,bool>& nonterm
)
1440 for(const auto& nt
: nonterm
) {
1442 reconnectIfNeeded();
1444 d_InsertEmptyNonTerminalOrderQuery_stmt
->
1445 bind("domain_id",domain_id
)->
1446 bind("qname", nt
.first
)->
1447 bindNull("ordername")->
1448 bind("auth",(nt
.second
|| !d_dnssecQueries
))->
1452 catch (SSqlException
&e
) {
1453 throw PDNSException("GSQLBackend unable to feed empty non-terminal with name '" + nt
.first
.toLogString() + "': "+e
.txtReason());
1459 bool GSQLBackend::feedEnts3(int domain_id
, const DNSName
&domain
, map
<DNSName
,bool> &nonterm
, const NSEC3PARAMRecordContent
& ns3prc
, bool narrow
)
1461 if(!d_dnssecQueries
)
1466 for(const auto& nt
: nonterm
) {
1468 reconnectIfNeeded();
1470 d_InsertEmptyNonTerminalOrderQuery_stmt
->
1471 bind("domain_id",domain_id
)->
1472 bind("qname", nt
.first
);
1473 if (narrow
|| !nt
.second
) {
1474 d_InsertEmptyNonTerminalOrderQuery_stmt
->
1475 bindNull("ordername");
1477 ordername
=toBase32Hex(hashQNameWithSalt(ns3prc
, nt
.first
));
1478 d_InsertEmptyNonTerminalOrderQuery_stmt
->
1479 bind("ordername", ordername
);
1481 d_InsertEmptyNonTerminalOrderQuery_stmt
->
1482 bind("auth",nt
.second
)->
1486 catch (SSqlException
&e
) {
1487 throw PDNSException("GSQLBackend unable to feed empty non-terminal with name '" + nt
.first
.toLogString() + "' (hashed name '"+ toBase32Hex(hashQNameWithSalt(ns3prc
, nt
.first
)) + "') : "+e
.txtReason());
1493 bool GSQLBackend::startTransaction(const DNSName
&domain
, int domain_id
)
1496 reconnectIfNeeded();
1498 d_db
->startTransaction();
1499 d_inTransaction
= true;
1500 if(domain_id
>= 0) {
1501 d_DeleteZoneQuery_stmt
->
1502 bind("domain_id", domain_id
)->
1507 catch (SSqlException
&e
) {
1508 d_inTransaction
= false;
1509 throw PDNSException("Database failed to start transaction for domain '" + domain
.toLogString() + "': "+e
.txtReason());
1515 bool GSQLBackend::commitTransaction()
1519 d_inTransaction
= false;
1521 catch (SSqlException
&e
) {
1522 d_inTransaction
= false;
1523 throw PDNSException("Database failed to commit transaction: "+e
.txtReason());
1528 bool GSQLBackend::abortTransaction()
1532 d_inTransaction
= false;
1534 catch(SSqlException
&e
) {
1535 d_inTransaction
= false;
1536 throw PDNSException("Database failed to abort transaction: "+string(e
.txtReason()));
1541 bool GSQLBackend::listComments(const uint32_t domain_id
)
1544 reconnectIfNeeded();
1546 d_query_name
= "list-comments-query";
1547 d_query_stmt
= &d_ListCommentsQuery_stmt
;
1549 bind("domain_id", domain_id
)->
1552 catch(SSqlException
&e
) {
1553 throw PDNSException("GSQLBackend unable to list comments for domain id " + std::to_string(domain_id
) + ": "+e
.txtReason());
1559 bool GSQLBackend::getComment(Comment
& comment
)
1561 SSqlStatement::row_t row
;
1564 if (!(*d_query_stmt
)->hasNextRow()) {
1566 (*d_query_stmt
)->reset();
1567 } catch(SSqlException
&e
) {
1568 throw PDNSException("GSQLBackend comment get: "+e
.txtReason());
1570 d_query_stmt
= NULL
;
1575 (*d_query_stmt
)->nextRow(row
);
1576 ASSERT_ROW_COLUMNS(d_query_name
, row
, 6);
1577 } catch(SSqlException
&e
) {
1578 throw PDNSException("GSQLBackend comment get: "+e
.txtReason());
1581 extractComment(row
, comment
);
1589 void GSQLBackend::feedComment(const Comment
& comment
)
1592 reconnectIfNeeded();
1594 d_InsertCommentQuery_stmt
->
1595 bind("domain_id",comment
.domain_id
)->
1596 bind("qname",comment
.qname
)->
1597 bind("qtype",comment
.qtype
.getName())->
1598 bind("modified_at",comment
.modified_at
)->
1599 bind("account",comment
.account
)->
1600 bind("content",comment
.content
)->
1604 catch (SSqlException
&e
) {
1605 throw PDNSException("GSQLBackend unable to feed comment for RRSet '" + comment
.qname
.toLogString() + "|" + comment
.qtype
.getName() + "': "+e
.txtReason());
1609 bool GSQLBackend::replaceComments(const uint32_t domain_id
, const DNSName
& qname
, const QType
& qt
, const vector
<Comment
>& comments
)
1612 reconnectIfNeeded();
1614 d_DeleteCommentRRsetQuery_stmt
->
1615 bind("domain_id",domain_id
)->
1616 bind("qname", qname
)->
1617 bind("qtype",qt
.getName())->
1621 catch (SSqlException
&e
) {
1622 throw PDNSException("GSQLBackend unable to delete comment for RRSet '" + qname
.toLogString() + "|" + qt
.getName() + "': "+e
.txtReason());
1625 for(const auto& comment
: comments
) {
1626 feedComment(comment
);
1632 string
GSQLBackend::directBackendCmd(const string
&query
)
1637 auto stmt
= d_db
->prepare(query
,0);
1639 reconnectIfNeeded();
1643 SSqlStatement::row_t row
;
1645 while(stmt
->hasNextRow()) {
1647 for(const auto& col
: row
)
1648 out
<<"\'"<<col
<<"\'\t";
1654 catch (SSqlException
&e
) {
1655 throw PDNSException("GSQLBackend unable to execute direct command query '" + query
+ "': "+e
.txtReason());
1659 string
GSQLBackend::pattern2SQLPattern(const string
&pattern
)
1661 string escaped_pattern
= boost::replace_all_copy(pattern
,"\\","\\\\");
1662 boost::replace_all(escaped_pattern
,"_","\\_");
1663 boost::replace_all(escaped_pattern
,"%","\\%");
1664 boost::replace_all(escaped_pattern
,"*","%");
1665 boost::replace_all(escaped_pattern
,"?","_");
1666 return escaped_pattern
;
1669 bool GSQLBackend::searchRecords(const string
&pattern
, int maxResults
, vector
<DNSResourceRecord
>& result
)
1672 string escaped_pattern
= pattern2SQLPattern(pattern
);
1674 reconnectIfNeeded();
1676 d_SearchRecordsQuery_stmt
->
1677 bind("value", escaped_pattern
)->
1678 bind("value2", escaped_pattern
)->
1679 bind("limit", maxResults
)->
1682 while(d_SearchRecordsQuery_stmt
->hasNextRow())
1684 SSqlStatement::row_t row
;
1685 DNSResourceRecord r
;
1686 d_SearchRecordsQuery_stmt
->nextRow(row
);
1687 ASSERT_ROW_COLUMNS("search-records-query", row
, 8);
1689 extractRecord(row
, r
);
1693 result
.push_back(r
);
1696 d_SearchRecordsQuery_stmt
->reset();
1700 catch (SSqlException
&e
) {
1701 throw PDNSException("GSQLBackend unable to search for records with pattern '" + pattern
+ "' (escaped pattern '" + escaped_pattern
+ "'): "+e
.txtReason());
1707 bool GSQLBackend::searchComments(const string
&pattern
, int maxResults
, vector
<Comment
>& result
)
1710 string escaped_pattern
= pattern2SQLPattern(pattern
);
1712 reconnectIfNeeded();
1714 d_SearchCommentsQuery_stmt
->
1715 bind("value", escaped_pattern
)->
1716 bind("value2", escaped_pattern
)->
1717 bind("limit", maxResults
)->
1720 while(d_SearchCommentsQuery_stmt
->hasNextRow()) {
1721 SSqlStatement::row_t row
;
1722 d_SearchCommentsQuery_stmt
->nextRow(row
);
1723 ASSERT_ROW_COLUMNS("search-comments-query", row
, 6);
1725 extractComment(row
, comment
);
1726 result
.push_back(comment
);
1729 d_SearchCommentsQuery_stmt
->reset();
1733 catch (SSqlException
&e
) {
1734 throw PDNSException("GSQLBackend unable to search for comments with pattern '" + pattern
+ "' (escaped pattern '" + escaped_pattern
+ "'): "+e
.txtReason());
1740 void GSQLBackend::extractRecord(const SSqlStatement::row_t
& row
, DNSResourceRecord
& r
)
1743 r
.ttl
= ::arg().asNum( "default-ttl" );
1745 r
.ttl
=pdns_stou(row
[1]);
1746 if(!d_qname
.empty())
1749 r
.qname
=DNSName(row
[6]);
1753 if (r
.qtype
==QType::MX
|| r
.qtype
==QType::SRV
)
1754 r
.content
=row
[2]+" "+row
[0];
1761 r
.auth
= !row
[7].empty() && row
[7][0]=='1';
1765 r
.disabled
= !row
[5].empty() && row
[5][0]=='1';
1767 r
.domain_id
=pdns_stou(row
[4]);
1770 void GSQLBackend::extractComment(const SSqlStatement::row_t
& row
, Comment
& comment
)
1772 comment
.domain_id
= pdns_stou(row
[0]);
1773 comment
.qname
= DNSName(row
[1]);
1774 comment
.qtype
= row
[2];
1775 comment
.modified_at
= pdns_stou(row
[3]);
1776 comment
.account
= row
[4];
1777 comment
.content
= row
[5];
1780 SSqlStatement::~SSqlStatement() {
1781 // make sure vtable won't break