]> git.ipfire.org Git - thirdparty/pdns.git/blame - modules/gmysqlbackend/gmysqlbackend.cc
small fixes
[thirdparty/pdns.git] / modules / gmysqlbackend / gmysqlbackend.cc
CommitLineData
343546e5 1// $Id: gmysqlbackend.cc,v 1.4 2002/12/09 18:34:45 ahu Exp $
2ffc06cb
BH
2#include <string>
3#include <map>
4
5using namespace std;
6
7#include "dns.hh"
8#include "dnsbackend.hh"
9#include "gmysqlbackend.hh"
10#include "dnspacket.hh"
11#include "ueberbackend.hh"
12#include "ahuexception.hh"
13#include "logger.hh"
14#include "arguments.hh"
99c45e0a
BH
15
16#ifdef PDNS_DOMYSQL
2ffc06cb 17#include "smysql.hh"
99c45e0a 18#endif
fc3b4b9d 19
99c45e0a
BH
20#ifdef PDNS_DOPGSQL
21#include "spgsql.hh"
fc3b4b9d
BH
22#endif
23
2ffc06cb
BH
24#include <sstream>
25
26
27void gMySQLBackend::setNotified(u_int32_t domain_id, u_int32_t serial)
28{
29 try {
30 d_db->doQuery("update domains set notified_serial="+itoa(serial)+" where id="+itoa(domain_id));
31 }
32 catch(SSqlException &e) {
33 throw AhuException("gMySQLBackend unable to refresh domain_id "+itoa(domain_id)+": "+e.txtReason());
34 }
35}
36
37void gMySQLBackend::setFresh(u_int32_t domain_id)
38{
39 try {
40 d_db->doQuery("update domains set last_check="+itoa(time(0))+" where id="+itoa(domain_id));
41 }
42 catch (SSqlException &e) {
43 throw AhuException("gMySQLBackend unable to refresh domain_id "+itoa(domain_id)+": "+e.txtReason());
44 }
45}
46
47bool gMySQLBackend::isMaster(const string &domain, const string &ip)
48{
49 try {
50 d_db->doQuery("select master from domains where name='"+sqlEscape(domain)+"' and type='SLAVE'", d_result);
51 }
52 catch (SSqlException &e) {
53 throw AhuException("gMySQLBackend unable to retrieve list of slave domains: "+e.txtReason());
54 }
55
56 if(d_result.empty())
57 return 0;
58
59 return !strcmp(ip.c_str(),d_result[0][0].c_str());
60}
61
62bool gMySQLBackend::getDomainInfo(const string &domain, DomainInfo &di)
63{
64 /* list all domains that need refreshing for which we are slave, and insert into SlaveDomain:
65 id,name,master IP,serial */
66
67 try {
68 d_db->doQuery("select id,name,master,last_check,notified_serial,type from domains where name='"+sqlEscape(domain)+"'",d_result);
69 }
70 catch(SSqlException &e) {
71 throw AhuException("gMySQLBackend unable to retrieve information about a domain: "+e.txtReason());
72 }
73
74 int numanswers=d_result.size();
75 if(!numanswers)
76 return false;
77
78 di.id=atol(d_result[0][0].c_str());
79 di.zone=d_result[0][1];
80 di.master=d_result[0][2];
81 di.last_check=atol(d_result[0][3].c_str());
82 di.backend=this;
83
84 string type=d_result[0][4];
85 if(type=="SLAVE")
86 di.kind=DomainInfo::Slave;
87 else if(type=="MASTER")
88 di.kind=DomainInfo::Slave;
89 else
90 di.kind=DomainInfo::Native;
91
92 return true;
93}
94
95void gMySQLBackend::getUnfreshSlaveInfos(vector<DomainInfo> *unfreshDomains)
96{
97 /* list all domains that need refreshing for which we are slave, and insert into SlaveDomain:
98 id,name,master IP,serial */
99
100 try {
101 d_db->doQuery("select id,name,master,last_check,type from domains where type='SLAVE'",d_result);
102 }
103 catch (SSqlException &e) {
104 throw AhuException("gMySQLBackend unable to retrieve list of slave domains: "+e.txtReason());
105 }
106
107 vector<DomainInfo>allSlaves;
108 int numanswers=d_result.size();
109 for(int n=0;n<numanswers;++n) { // id,name,master,last_check
110 DomainInfo sd;
111 sd.id=atol(d_result[n][0].c_str());
112 sd.zone=d_result[n][1];
113 sd.master=d_result[n][2];
114 sd.last_check=atol(d_result[n][3].c_str());
115 sd.backend=this;
116 sd.kind=DomainInfo::Slave;
117 allSlaves.push_back(sd);
118 }
119
120 for(vector<DomainInfo>::iterator i=allSlaves.begin();i!=allSlaves.end();++i) {
121 SOAData sdata;
122 sdata.serial=0;
123 sdata.refresh=0;
124 getSOA(i->zone,sdata);
125 if(i->last_check+sdata.refresh<time(0)) {
126 i->serial=sdata.serial;
127 unfreshDomains->push_back(*i);
128 }
129 }
130}
131
132void gMySQLBackend::getUpdatedMasters(vector<DomainInfo> *updatedDomains)
133{
134 /* list all domains that need notifications for which we are master, and insert into updatedDomains
135 id,name,master IP,serial */
136
137 try {
138 d_db->doQuery("select id,name,master,last_check,notified_serial,type from domains where type='MASTER'",d_result);
139 }
140 catch(SSqlException &e) {
141 throw AhuException("gMySQLBackend unable to retrieve list of master domains: "+e.txtReason());
142 }
143
144 vector<DomainInfo>allMasters;
145 int numanswers=d_result.size();
146 for(int n=0;n<numanswers;++n) { // id,name,master,last_check
147 DomainInfo sd;
148 sd.id=atol(d_result[n][0].c_str());
149 sd.zone=d_result[n][1];
150 sd.master=d_result[n][2];
151 sd.last_check=atol(d_result[n][3].c_str());
152 sd.notified_serial=atoi(d_result[n][4].c_str());
153 sd.backend=this;
154 sd.kind=DomainInfo::Master;
155 allMasters.push_back(sd);
156 }
157
158 for(vector<DomainInfo>::iterator i=allMasters.begin();i!=allMasters.end();++i) {
159 SOAData sdata;
160 sdata.serial=0;
161 sdata.refresh=0;
162 getSOA(i->zone,sdata);
163 if(i->notified_serial!=sdata.serial) {
164 i->serial=sdata.serial;
165 updatedDomains->push_back(*i);
166 }
167 }
168}
169
170
171string gMySQLBackend::sqlEscape(const string &name)
172{
173 string a;
174
175 for(string::const_iterator i=name.begin();i!=name.end();++i)
176 if(*i=='\'' || *i=='\\'){
177 a+='\\';
178 a+=*i;
179 }
180 else
181 a+=*i;
182 return a;
183}
184
185
186gMySQLBackend::gMySQLBackend(const string &mode, const string &suffix)
187{
188 setArgPrefix(mode+suffix);
189
190 d_logprefix="["+mode+"Backend"+suffix+"] ";
191 d_db=0;
192 try {
99c45e0a
BH
193 if(0) {}
194#ifdef PDNS_DOMYSQL
195 else if(mode=="gmysql")
2ffc06cb
BH
196 d_db=new SMySQL(getArg("dbname"),
197 getArg("host"),
198 getArg("socket"),
199 getArg("user"),
200 getArg("password"));
99c45e0a
BH
201#endif
202#ifdef PDNS_DOPGSQL
203 else if(mode=="gpgsql")
2ffc06cb
BH
204 d_db=new SPgSQL(getArg("dbname"),
205 getArg("host"),
206 getArg("socket"),
207 getArg("user"),
208 getArg("password"));
fc3b4b9d 209#endif
2ffc06cb
BH
210 else {
211 L<<Logger::Error<<d_logprefix<<"Generic backend does not support database '"<<mode<<"'"<<endl;
212 exit(1);
213 }
214
215 }
216 catch(SSqlException &e) {
217 L<<Logger::Error<<d_logprefix<<"Connection failed: "<<e.txtReason()<<endl;
218 throw AhuException("Unable to launch "+mode+" connection: "+e.txtReason());
219 }
220
221 d_noWildCardNoIDQuery=getArg("basic-query");
222 d_noWildCardIDQuery=getArg("id-query");
223 d_wildCardNoIDQuery=getArg("wildcard-query");
224 d_wildCardIDQuery=getArg("wildcard-id-query");
225
226 d_noWildCardANYNoIDQuery=getArg("any-query");
227 d_noWildCardANYIDQuery=getArg("any-id-query");
228 d_wildCardANYNoIDQuery=getArg("wildcard-any-query");
229 d_wildCardANYIDQuery=getArg("wildcard-any-id-query");
230
231 d_listQuery=getArg("list-query");
232 L<<Logger::Warning<<d_logprefix<<"Connection succesful"<<endl;
233}
234
235gMySQLBackend::~gMySQLBackend()
236{
237 if(d_db)
238 delete d_db;
239 L<<Logger::Error<<d_logprefix<<"Closing connection"<<endl;
240}
241
242void gMySQLBackend::lookup(const QType &qtype,const string &qname, DNSPacket *pkt_p, int domain_id)
243{
244 string format;
245 char output[1024];
246
247 d_db->setLog(arg().mustDo("query-logging"));
248
249 string lcqname=toLower(qname);
250
251 if(qtype.getCode()!=QType::ANY) {
252 // qtype qname domain_id
253 if(domain_id<0) {
254 if(qname[0]=='%')
255 format=d_wildCardNoIDQuery;
256 else
257 format=d_noWildCardNoIDQuery;
258
259 snprintf(output,1023, format.c_str(),sqlEscape(qtype.getName()).c_str(), sqlEscape(lcqname).c_str());
260 }
261 else {
262 if(qname[0]!='%')
263 format=d_noWildCardIDQuery;
264 else
265 format=d_wildCardIDQuery;
266 snprintf(output,1023, format.c_str(),sqlEscape(qtype.getName()).c_str(),sqlEscape(lcqname).c_str(),domain_id);
267 }
268 }
269 else {
270 // qtype==ANY
271 // qname domain_id
272 if(domain_id<0) {
273 if(qname[0]=='%')
274 format=d_wildCardANYNoIDQuery;
275 else
276 format=d_noWildCardANYNoIDQuery;
277
278 snprintf(output,1023, format.c_str(),sqlEscape(lcqname).c_str());
279 }
280 else {
281 if(qname[0]!='%')
282 format=d_noWildCardANYIDQuery;
283 else
284 format=d_wildCardANYIDQuery;
285 snprintf(output,1023, format.c_str(),sqlEscape(lcqname).c_str(),domain_id);
286 }
287 }
288 DLOG(L<< "Query: '" << output << "'"<<endl);
289
290 try {
291 d_db->doQuery(output);
292 }
293 catch(SSqlException &e) {
294 throw AhuException(e.txtReason());
295 }
296
297 d_qname=qname;
298
299 d_qtype=qtype;
300 d_count=0;
301}
302bool gMySQLBackend::list(int domain_id )
303{
304 DLOG(L<<"gMySQLBackend constructing handle for list of domain id'"<<domain_id<<"'"<<endl);
305
306 char output[1024];
307 snprintf(output,1023,d_listQuery.c_str(),domain_id);
308 try {
309 d_db->doQuery(output);
310 }
311 catch(SSqlException &e) {
312 throw AhuException("gMySQLBackend list query: "+e.txtReason());
313 }
314
315 d_qname="";
316 d_count=0;
317 return true;
318}
319
320bool gMySQLBackend::superMasterBackend(const string &ip, const string &domain, const vector<DNSResourceRecord>&nsset, string *account, DNSBackend **ddb)
321{
322 // check if we know the ip/ns couple in the database
323 for(vector<DNSResourceRecord>::const_iterator i=nsset.begin();i!=nsset.end();++i) {
324 try {
325 d_db->doQuery(("select account from supermasters where ip='"+sqlEscape(ip)+"' and nameserver='"+sqlEscape(i->content)+"'"),
326 d_result);
327 }
328 catch (SSqlException &e) {
329 throw AhuException("gMySQLBackend unable to search for a domain: "+e.txtReason());
330 }
331
332 if(!d_result.empty()) {
333 *account=d_result[0][0];
334 *ddb=this;
335 return true;
336 }
337 }
338 return false;
339}
340
341bool gMySQLBackend::createSlaveDomain(const string &ip, const string &domain, const string &account)
342{
343 try {
344 d_db->doQuery(("insert into domains (type,name,master,account) values('SLAVE','"+
345 sqlEscape(domain)+"','"+
346 sqlEscape(ip)+"','"+sqlEscape(account)+"')"));
347 }
348 catch(SSqlException &e) {
349 throw AhuException("Database error trying to insert new slave '"+domain+"': "+ e.txtReason());
350 }
351 return true;
352}
353
354
355bool gMySQLBackend::get(DNSResourceRecord &r)
356{
357 // L << "gMySQLBackend get() was called for "<<qtype.getName() << " record: ";
343546e5 358 SSql::row_t row;
2ffc06cb
BH
359 if(d_db->getRow(row)) {
360 r.content=row[0];
361 r.ttl=atol(row[1].c_str());
362 r.priority=atol(row[2].c_str());
363 if(!d_qname.empty())
364 r.qname=d_qname;
365 else
366 r.qname=row[5];
367 r.qtype=row[3];
368
369 r.domain_id=atoi(row[4].c_str());
370 return true;
371 }
372
373 return false;
374}
375
376bool gMySQLBackend::feedRecord(const DNSResourceRecord &r)
377{
378 ostringstream os;
379
380 os<<"insert into records (content,ttl,prio,type,domain_id,name) values ('"<<
381 sqlEscape(r.content)<<"', "<<
382 r.ttl<<", "<<
383 r.priority<<", '"<<sqlEscape(r.qtype.getName())<<"', "<<
384 r.domain_id<<
385 ", '"<<sqlEscape(r.qname)<<"')";
386
387 // L<<Logger::Error<<"Trying: '"<<os.str()<<"'"<<endl;
388
389 try {
390 d_db->doQuery(os.str());
391 }
392 catch (SSqlException &e) {
393 throw AhuException(e.txtReason());
394 }
395
396}
397
398bool gMySQLBackend::startTransaction(const string &domain, int domain_id)
399{
400 try {
401 d_db->doQuery("begin");
402 d_db->doQuery("delete from records where domain_id="+itoa(domain_id));
403 }
404 catch (SSqlException &e) {
405 throw AhuException("Database failed to start transaction: "+e.txtReason());
406 }
407
408 return true;
409}
410
411bool gMySQLBackend::commitTransaction()
412{
413 try {
414 d_db->doQuery("commit");
415 }
416 catch (SSqlException &e) {
417 throw AhuException("Database failed to commit transaction: "+e.txtReason());
418 }
419 return true;
420}
421
422bool gMySQLBackend::abortTransaction()
423{
424 try {
425 d_db->doQuery("rollback");
426 }
427 catch(SSqlException &e) {
428 throw AhuException("MySQL failed to abort transaction: "+string(e.txtReason()));
429 }
430 return true;
431}
432
433
434class gMySQLFactory : public BackendFactory
435{
436public:
437 gMySQLFactory(const string &mode) : BackendFactory(mode),d_mode(mode) {}
438
439 void declareArguments(const string &suffix="")
440 {
441 declare(suffix,"dbname","Pdns backend database name to connect to","powerdns");
442 declare(suffix,"user","Pdns backend user to connect as","powerdns");
443 declare(suffix,"host","Pdns backend host to connect to","");
444 declare(suffix,"socket","Pdns backend socket to connect to","");
445 declare(suffix,"password","Pdns backend password to connect with","");
446
447 declare(suffix,"basic-query","Basic query","select content,ttl,prio,type,domain_id,name from records where type='%s' and name='%s'");
448 declare(suffix,"id-query","Basic with ID query","select content,ttl,prio,type,domain_id,name from records where type='%s' and name='%s' and domain_id=%d");
449 declare(suffix,"wildcard-query","Wildcard query","select content,ttl,prio,type,domain_id,name from records where type='%s' and name like '%s'");
450 declare(suffix,"wildcard-id-query","Wildcard with ID query","select content,ttl,prio,type,domain_id,name from records where type='%s' and name like '%s' and domain_id='%d'");
451
452 declare(suffix,"any-query","Any query","select content,ttl,prio,type,domain_id,name from records where name='%s'");
453 declare(suffix,"any-id-query","Any with ID query","select content,ttl,prio,type,domain_id,name from records where name='%s' and domain_id=%d");
454 declare(suffix,"wildcard-any-query","Wildcard ANY query","select content,ttl,prio,type,domain_id,name from records where name like '%s'");
455 declare(suffix,"wildcard-any-id-query","Wildcard ANY with ID query","select content,ttl,prio,type,domain_id,name from records where like '%s' and domain_id='%d'");
456
457 declare(suffix,"list-query","AXFR query", "select content,ttl,prio,type,domain_id,name from records where domain_id='%d'");
458
459 }
460
461 DNSBackend *make(const string &suffix="")
462 {
463 return new gMySQLBackend(d_mode,suffix);
464 }
465private:
466 const string d_mode;
467};
468
469
470//! Magic class that is activated when the dynamic library is loaded
471class gMySQLLoader
472{
473public:
474 //! This reports us to the main UeberBackend class
475 gMySQLLoader()
476 {
477 BackendMakers().report(new gMySQLFactory("gmysql"));
478 BackendMakers().report(new gMySQLFactory("gpgsql2"));
479 L<<Logger::Warning<<"This is module gmysqlbackend.so reporting"<<endl;
480 }
481};
482static gMySQLLoader gmysqlloader;