]> granicus.if.org Git - pdns/commitdiff
generic sql stuff
authorBert Hubert <bert.hubert@netherlabs.nl>
Mon, 16 Dec 2002 20:34:29 +0000 (20:34 +0000)
committerBert Hubert <bert.hubert@netherlabs.nl>
Mon, 16 Dec 2002 20:34:29 +0000 (20:34 +0000)
git-svn-id: svn://svn.powerdns.com/pdns/trunk/pdns@81 d19b8d6e-7fed-0310-83ef-9ca221ded41b

pdns/backends/gsql/gsqlbackend.cc [new file with mode: 0644]
pdns/backends/gsql/gsqlbackend.hh [new file with mode: 0644]
pdns/backends/gsql/ssql.hh [new file with mode: 0644]

diff --git a/pdns/backends/gsql/gsqlbackend.cc b/pdns/backends/gsql/gsqlbackend.cc
new file mode 100644 (file)
index 0000000..13065c1
--- /dev/null
@@ -0,0 +1,426 @@
+// $Id: gsqlbackend.cc,v 1.1 2002/12/16 20:34:29 ahu Exp $ 
+#include <string>
+#include <map>
+
+using namespace std;
+
+#include "pdns/dns.hh"
+#include "pdns/dnsbackend.hh"
+#include "gsqlbackend.hh"
+#include "pdns/dnspacket.hh"
+#include "pdns/ueberbackend.hh"
+#include "pdns/ahuexception.hh"
+#include "pdns/logger.hh"
+#include "pdns/arguments.hh"
+
+
+
+#include <sstream>
+
+void GSQLBackend::setNotified(u_int32_t domain_id, u_int32_t serial)
+{
+  try {
+    d_db->doQuery("update domains set notified_serial="+itoa(serial)+" where id="+itoa(domain_id));
+  }
+  catch(SSqlException &e) {
+    throw AhuException("GSQLBackend unable to refresh domain_id "+itoa(domain_id)+": "+e.txtReason());
+  }
+}
+
+void GSQLBackend::setFresh(u_int32_t domain_id)
+{
+  try {
+    d_db->doQuery("update domains set last_check="+itoa(time(0))+" where id="+itoa(domain_id));
+  }
+  catch (SSqlException &e) {
+    throw AhuException("GSQLBackend unable to refresh domain_id "+itoa(domain_id)+": "+e.txtReason());
+  }
+}
+
+bool GSQLBackend::isMaster(const string &domain, const string &ip)
+{
+  try {
+    d_db->doQuery("select master from domains where name='"+sqlEscape(domain)+"' and type='SLAVE'", d_result);
+  }
+  catch (SSqlException &e) {
+    throw AhuException("GSQLBackend unable to retrieve list of slave domains: "+e.txtReason());
+  }
+
+  if(d_result.empty())
+    return 0;
+  
+  return !strcmp(ip.c_str(),d_result[0][0].c_str());
+}
+
+bool GSQLBackend::getDomainInfo(const string &domain, DomainInfo &di)
+{
+  /* list all domains that need refreshing for which we are slave, and insert into SlaveDomain:
+     id,name,master IP,serial */
+  
+  try {
+    d_db->doQuery("select id,name,master,last_check,notified_serial,type from domains where name='"+sqlEscape(domain)+"'",d_result);
+  }
+  catch(SSqlException &e) {
+    throw AhuException("GSQLBackend unable to retrieve information about a domain: "+e.txtReason());
+  }
+
+  int numanswers=d_result.size();
+  if(!numanswers)
+    return false;
+  
+  di.id=atol(d_result[0][0].c_str());
+  di.zone=d_result[0][1];
+  di.master=d_result[0][2];
+  di.last_check=atol(d_result[0][3].c_str());
+  di.backend=this;
+  
+  string type=d_result[0][4];
+  if(type=="SLAVE")
+    di.kind=DomainInfo::Slave;
+  else if(type=="MASTER")
+    di.kind=DomainInfo::Slave;
+  else 
+    di.kind=DomainInfo::Native;
+  
+  return true;
+}
+
+void GSQLBackend::getUnfreshSlaveInfos(vector<DomainInfo> *unfreshDomains)
+{
+  /* list all domains that need refreshing for which we are slave, and insert into SlaveDomain:
+     id,name,master IP,serial */
+
+  try {
+    d_db->doQuery("select id,name,master,last_check,type from domains where type='SLAVE'",d_result);
+  }
+  catch (SSqlException &e) {
+    throw AhuException("GSQLBackend unable to retrieve list of slave domains: "+e.txtReason());
+  }
+
+  vector<DomainInfo>allSlaves;
+  int numanswers=d_result.size();
+  for(int n=0;n<numanswers;++n) { // id,name,master,last_check
+    DomainInfo sd;
+    sd.id=atol(d_result[n][0].c_str());
+    sd.zone=d_result[n][1];
+    sd.master=d_result[n][2];
+    sd.last_check=atol(d_result[n][3].c_str());
+    sd.backend=this;
+    sd.kind=DomainInfo::Slave;
+    allSlaves.push_back(sd);
+  }
+
+  for(vector<DomainInfo>::iterator i=allSlaves.begin();i!=allSlaves.end();++i) {
+    SOAData sdata;
+    sdata.serial=0;
+    sdata.refresh=0;
+    getSOA(i->zone,sdata);
+    if((time_t)(i->last_check+sdata.refresh) < time(0)) {
+      i->serial=sdata.serial;
+      unfreshDomains->push_back(*i);
+    }
+  }
+}
+
+void GSQLBackend::getUpdatedMasters(vector<DomainInfo> *updatedDomains)
+{
+  /* list all domains that need notifications for which we are master, and insert into updatedDomains
+     id,name,master IP,serial */
+
+  try {
+    d_db->doQuery("select id,name,master,last_check,notified_serial,type from domains where type='MASTER'",d_result);
+  }
+  catch(SSqlException &e) {
+    throw AhuException("GSQLBackend unable to retrieve list of master domains: "+e.txtReason());
+  }
+
+  vector<DomainInfo>allMasters;
+  int numanswers=d_result.size();
+  for(int n=0;n<numanswers;++n) { // id,name,master,last_check
+    DomainInfo sd;
+    sd.id=atol(d_result[n][0].c_str());
+    sd.zone=d_result[n][1];
+    sd.master=d_result[n][2];
+    sd.last_check=atol(d_result[n][3].c_str());
+    sd.notified_serial=atoi(d_result[n][4].c_str());
+    sd.backend=this;
+    sd.kind=DomainInfo::Master;
+    allMasters.push_back(sd);
+  }
+
+  for(vector<DomainInfo>::iterator i=allMasters.begin();i!=allMasters.end();++i) {
+    SOAData sdata;
+    sdata.serial=0;
+    sdata.refresh=0;
+    getSOA(i->zone,sdata);
+    if(i->notified_serial!=sdata.serial) {
+      i->serial=sdata.serial;
+      updatedDomains->push_back(*i);
+    }
+  }
+}
+
+
+string GSQLBackend::sqlEscape(const string &name)
+{
+  string a;
+
+  for(string::const_iterator i=name.begin();i!=name.end();++i)
+    if(*i=='\'' || *i=='\\'){
+      a+='\\';
+      a+=*i;
+    }
+    else
+      a+=*i;
+  return a;
+}
+
+
+GSQLBackend::GSQLBackend(const string &mode, const string &suffix)
+{
+  setArgPrefix(mode+suffix);
+
+  d_logprefix="["+mode+"Backend"+suffix+"] ";
+                 
+  d_noWildCardNoIDQuery=getArg("basic-query");
+  d_noWildCardIDQuery=getArg("id-query");
+  d_wildCardNoIDQuery=getArg("wildcard-query");
+  d_wildCardIDQuery=getArg("wildcard-id-query");
+
+  d_noWildCardANYNoIDQuery=getArg("any-query");
+  d_noWildCardANYIDQuery=getArg("any-id-query");
+  d_wildCardANYNoIDQuery=getArg("wildcard-any-query");
+  d_wildCardANYIDQuery=getArg("wildcard-any-id-query");
+  
+  d_listQuery=getArg("list-query");
+}
+
+
+void GSQLBackend::lookup(const QType &qtype,const string &qname, DNSPacket *pkt_p, int domain_id)
+{
+  string format;
+  char output[1024];
+
+  d_db->setLog(arg().mustDo("query-logging"));
+
+  string lcqname=toLower(qname);
+  
+  if(qtype.getCode()!=QType::ANY) {
+    // qtype qname domain_id
+    if(domain_id<0) {
+      if(qname[0]=='%')
+       format=d_wildCardNoIDQuery;
+      else
+       format=d_noWildCardNoIDQuery;
+
+      snprintf(output,1023, format.c_str(),sqlEscape(qtype.getName()).c_str(), sqlEscape(lcqname).c_str());
+    }
+    else {
+      if(qname[0]!='%')
+       format=d_noWildCardIDQuery;
+      else
+       format=d_wildCardIDQuery;
+      snprintf(output,1023, format.c_str(),sqlEscape(qtype.getName()).c_str(),sqlEscape(lcqname).c_str(),domain_id);
+    }
+  }
+  else {
+    // qtype==ANY
+    // qname domain_id
+    if(domain_id<0) {
+      if(qname[0]=='%')
+       format=d_wildCardANYNoIDQuery;
+      else
+       format=d_noWildCardANYNoIDQuery;
+
+      snprintf(output,1023, format.c_str(),sqlEscape(lcqname).c_str());
+    }
+    else {
+      if(qname[0]!='%')
+       format=d_noWildCardANYIDQuery;
+      else
+       format=d_wildCardANYIDQuery;
+      snprintf(output,1023, format.c_str(),sqlEscape(lcqname).c_str(),domain_id);
+    }
+  }
+  DLOG(L<< "Query: '" << output << "'"<<endl);
+
+  try {
+    d_db->doQuery(output);
+  }
+  catch(SSqlException &e) {
+    throw AhuException(e.txtReason());
+  }
+
+  d_qname=qname;
+
+  d_qtype=qtype;
+  d_count=0;
+}
+bool GSQLBackend::list(int domain_id )
+{
+  DLOG(L<<"GSQLBackend constructing handle for list of domain id'"<<domain_id<<"'"<<endl);
+
+  char output[1024];
+  snprintf(output,1023,d_listQuery.c_str(),domain_id);
+  try {
+    d_db->doQuery(output);
+  }
+  catch(SSqlException &e) {
+    throw AhuException("GSQLBackend list query: "+e.txtReason());
+  }
+
+  d_qname="";
+  d_count=0;
+  return true;
+}
+
+bool GSQLBackend::superMasterBackend(const string &ip, const string &domain, const vector<DNSResourceRecord>&nsset, string *account, DNSBackend **ddb)
+{
+  // check if we know the ip/ns couple in the database
+  for(vector<DNSResourceRecord>::const_iterator i=nsset.begin();i!=nsset.end();++i) {
+    try {
+      d_db->doQuery(("select account from supermasters where ip='"+sqlEscape(ip)+"' and nameserver='"+sqlEscape(i->content)+"'"),
+                   d_result);
+    }
+    catch (SSqlException &e) {
+      throw AhuException("GSQLBackend unable to search for a domain: "+e.txtReason());
+    }
+
+    if(!d_result.empty()) {
+      *account=d_result[0][0];
+      *ddb=this;
+      return true;
+    }
+  }
+  return false;
+}
+
+bool GSQLBackend::createSlaveDomain(const string &ip, const string &domain, const string &account)
+{
+  try {
+    d_db->doQuery(("insert into domains (type,name,master,account) values('SLAVE','"+
+                  sqlEscape(domain)+"','"+
+                  sqlEscape(ip)+"','"+sqlEscape(account)+"')"));
+  }
+  catch(SSqlException &e) {
+    throw AhuException("Database error trying to insert new slave '"+domain+"': "+ e.txtReason());
+  }
+  return true;
+}
+
+
+bool GSQLBackend::get(DNSResourceRecord &r)
+{
+  // L << "GSQLBackend get() was called for "<<qtype.getName() << " record: ";
+  SSql::row_t row;
+  if(d_db->getRow(row)) {
+    r.content=row[0];
+    r.ttl=atol(row[1].c_str());
+    r.priority=atol(row[2].c_str());
+    if(!d_qname.empty())
+      r.qname=d_qname;
+    else
+      r.qname=row[5];
+    r.qtype=row[3];
+    
+    r.domain_id=atoi(row[4].c_str());
+    return true;
+  }
+  
+  return false;
+}
+
+bool GSQLBackend::feedRecord(const DNSResourceRecord &r)
+{
+  ostringstream os;
+  
+  os<<"insert into records (content,ttl,prio,type,domain_id,name) values ('"<<
+    sqlEscape(r.content)<<"', "<<
+    r.ttl<<", "<<
+    r.priority<<", '"<<sqlEscape(r.qtype.getName())<<"', "<<
+    r.domain_id<<
+    ", '"<<sqlEscape(r.qname)<<"')";
+  
+  //  L<<Logger::Error<<"Trying: '"<<os.str()<<"'"<<endl;
+
+  try {
+    d_db->doQuery(os.str());
+  }
+  catch (SSqlException &e) {
+    throw AhuException(e.txtReason());
+  }
+  return true; // XXX FIXME this API should not return 'true' I think -ahu 
+}
+
+bool GSQLBackend::startTransaction(const string &domain, int domain_id)
+{
+  try {
+    d_db->doQuery("begin");
+    d_db->doQuery("delete from records where domain_id="+itoa(domain_id));
+  }
+  catch (SSqlException &e) {
+    throw AhuException("Database failed to start transaction: "+e.txtReason());
+  }
+
+  return true;
+}
+
+bool GSQLBackend::commitTransaction()
+{
+  try {
+    d_db->doQuery("commit");
+  }
+  catch (SSqlException &e) {
+    throw AhuException("Database failed to commit transaction: "+e.txtReason());
+  }
+  return true;
+}
+
+bool GSQLBackend::abortTransaction()
+{
+  try {
+    d_db->doQuery("rollback");
+  }
+  catch(SSqlException &e) {
+    throw AhuException("MySQL failed to abort transaction: "+string(e.txtReason()));
+  }
+  return true;
+}
+
+#if 0
+class GSQLFactory : public BackendFactory
+{
+public:
+  GSQLFactory(const string &mode) : BackendFactory(mode),d_mode(mode) {}
+  
+  void declareArguments(const string &suffix="")
+  {
+    declare(suffix,"dbname","Pdns backend database name to connect to","powerdns");
+    declare(suffix,"user","Pdns backend user to connect as","powerdns");
+    declare(suffix,"host","Pdns backend host to connect to","");
+    declare(suffix,"socket","Pdns backend socket to connect to","");
+    declare(suffix,"password","Pdns backend password to connect with","");
+
+    declare(suffix,"basic-query","Basic query","select content,ttl,prio,type,domain_id,name from records where type='%s' and name='%s'");
+    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");
+    declare(suffix,"wildcard-query","Wildcard query","select content,ttl,prio,type,domain_id,name from records where type='%s' and name like '%s'");
+    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'");
+
+    declare(suffix,"any-query","Any query","select content,ttl,prio,type,domain_id,name from records where name='%s'");
+    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");
+    declare(suffix,"wildcard-any-query","Wildcard ANY query","select content,ttl,prio,type,domain_id,name from records where name like '%s'");
+    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'");
+
+    declare(suffix,"list-query","AXFR query", "select content,ttl,prio,type,domain_id,name from records where domain_id='%d'");
+
+  }
+  
+  DNSBackend *make(const string &suffix="")
+  {
+    return new GSQLBackend(d_mode,suffix);
+  }
+private:
+  const string d_mode;
+};
+
+#endif
diff --git a/pdns/backends/gsql/gsqlbackend.hh b/pdns/backends/gsql/gsqlbackend.hh
new file mode 100644 (file)
index 0000000..fdd3f91
--- /dev/null
@@ -0,0 +1,58 @@
+#include <string>
+#include <map>
+#include "ssql.hh"
+
+using namespace std;
+
+/** The GSQLBackend is a DNSBackend that can answer DNS related questions. It looks up data
+    in PostgreSQL */
+class GSQLBackend : public DNSBackend
+{
+public:
+  GSQLBackend(const string &mode, const string &suffix); //!< Makes our connection to the database. Throws an exception if it fails.
+  virtual ~GSQLBackend()
+  {
+    if(d_db)
+      delete d_db;
+  }
+  
+  void setDB(SSql *db)
+  {
+    d_db=db;
+  }
+  
+  string sqlEscape(const string &name);
+  void lookup(const QType &, const string &qdomain, DNSPacket *p=0, int zoneId=-1);
+  bool list(int domain_id);
+  bool get(DNSResourceRecord &r);
+  bool isMaster(const string &domain, const string &ip);
+
+  bool startTransaction(const string &domain, int domain_id=-1);
+  bool commitTransaction();
+  bool abortTransaction();
+  bool feedRecord(const DNSResourceRecord &r);
+  bool createSlaveDomain(const string &ip, const string &domain, const string &account);
+  bool superMasterBackend(const string &ip, const string &domain, const vector<DNSResourceRecord>&nsset, string *account, DNSBackend **db);
+  void setFresh(u_int32_t domain_id);
+  void getUnfreshSlaveInfos(vector<DomainInfo> *domains);
+  void getUpdatedMasters(vector<DomainInfo> *updatedDomains);
+  bool getDomainInfo(const string &domain, DomainInfo &di);
+  void setNotified(u_int32_t domain_id, u_int32_t serial);
+private:
+  string d_qname;
+  QType d_qtype;
+  int d_count;
+  SSql *d_db;
+  SSql::result_t d_result;
+
+  string d_wildCardNoIDQuery;
+  string d_noWildCardNoIDQuery;
+  string d_noWildCardIDQuery;
+  string d_wildCardIDQuery;
+  string d_wildCardANYNoIDQuery;
+  string d_noWildCardANYNoIDQuery;
+  string d_noWildCardANYIDQuery;
+  string d_wildCardANYIDQuery;
+  string d_listQuery;
+  string d_logprefix;
+};
diff --git a/pdns/backends/gsql/ssql.hh b/pdns/backends/gsql/ssql.hh
new file mode 100644 (file)
index 0000000..45dafe8
--- /dev/null
@@ -0,0 +1,42 @@
+/* Copyright 2001 Netherlabs BV, bert.hubert@netherlabs.nl. See LICENSE 
+   for more information.
+   $Id: ssql.hh,v 1.1 2002/12/16 20:34:29 ahu Exp $  */
+#ifndef SSQL_HH
+#define SSQL_HH
+
+#include <string>
+#include <vector>
+using namespace std;
+
+
+class SSqlException 
+{
+public: 
+  SSqlException(const string &reason) 
+  {
+      d_reason=reason;
+  }
+  
+  string txtReason()
+  {
+    return d_reason;
+  }
+private:
+  string d_reason;
+};
+
+class SSql
+{
+public:
+  typedef vector<string> row_t;
+  typedef vector<row_t> result_t;
+  virtual SSqlException sPerrorException(const string &reason)=0;
+  virtual int doQuery(const string &query, result_t &result)=0;
+  virtual int doQuery(const string &query)=0;
+  virtual bool getRow(row_t &row)=0;
+  virtual string escape(const string &name)=0;
+  virtual void setLog(bool state){}
+  virtual ~SSql(){};
+};
+
+#endif /* SSQL_HH */