]> granicus.if.org Git - pdns/commitdiff
add initial cut at working Generic Oracle backend
authorBert Hubert <bert.hubert@netherlabs.nl>
Fri, 8 Apr 2005 18:58:52 +0000 (18:58 +0000)
committerBert Hubert <bert.hubert@netherlabs.nl>
Fri, 8 Apr 2005 18:58:52 +0000 (18:58 +0000)
including documentation
Very lightly tested

git-svn-id: svn://svn.powerdns.com/pdns/trunk/pdns@337 d19b8d6e-7fed-0310-83ef-9ca221ded41b

configure.in
modules/goraclebackend/Makefile.am
modules/goraclebackend/goraclebackend.cc
modules/goraclebackend/goraclebackend.hh
modules/goraclebackend/soracle.cc
modules/goraclebackend/soracle.hh
pdns/docs/pdns.sgml

index dc3f30de1153a1e953051b05ef05b34a6acc7463..dac21de25ab781cc9d0089195c064bdad569dacd 100644 (file)
@@ -410,4 +410,4 @@ modules/geobackend/Makefile \
 modules/pipebackend/Makefile modules/oraclebackend/Makefile \
 modules/xdbbackend/Makefile modules/odbcbackend/Makefile \
 modules/gpgsqlbackend/Makefile modules/ldapbackend/Makefile 
-modules/gsqlitebackend/Makefile )
+modules/gsqlitebackend/Makefile modules/goraclebackend/Makefile )
index 4aab9c1d8b18dcfda15abac46562d9bfb54fb455..5fc1001911fd49bdf78d11c632bad17504f03d74 100644 (file)
@@ -1,7 +1,7 @@
-#lib_LTLIBRARIES = libgoraclebackend.la
-bin_PROGRAMS = soracle
+lib_LTLIBRARIES = libgoraclebackend.la
+#bin_PROGRAMS = soracle
 
-soracle_SOURCES=soracle.cc soracle.hh
+#soracle_SOURCES=soracle.cc soracle.hh 
 
 EXTRA_DIST=OBJECTFILES OBJECTLIBS
 
@@ -10,12 +10,12 @@ ORACLE_HOME ?= /opt/oracle
 INCLUDES= -I$(ORACLE_HOME)/rdbms/demo \
                  -I$(ORACLE_HOME)/rdbms/public 
 
-#libgoraclebackend_la_SOURCES=goraclebackend.cc goraclebackend.hh \
+libgoraclebackend_la_SOURCES=goraclebackend.cc goraclebackend.hh \
                soracle.hh soracle.cc
 
 
-#libgoraclebackend_la_LDFLAGS=-module  -L$(ORACLE_HOME)/lib -lclient9  -lclntst9
-soracle_LDFLAGS=-module  -L$(ORACLE_HOME)/lib -lclient9  -lclntst9 -ldl -pthread
+libgoraclebackend_la_LDFLAGS=-module  -L$(ORACLE_HOME)/lib -lclient9  -lclntst9 
+#soracle_LDFLAGS=-module  -L$(ORACLE_HOME)/lib -lclient9  -lclntst9 -ldl -pthread
 
 #      -Wl,-Bstatic -lpq++ -lpq -Wl,-Bdynamic -lssl -lcrypt -lcrypto
 
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5d4fbeb482b597dae6fc6f7b3a8ec24ba7b70e0d 100644 (file)
@@ -0,0 +1,101 @@
+
+#include <string>
+#include <map>
+
+using namespace std;
+
+#include "pdns/dns.hh"
+#include "pdns/dnsbackend.hh"
+#include "goraclebackend.hh"
+#include "pdns/dnspacket.hh"
+#include "pdns/ueberbackend.hh"
+#include "pdns/ahuexception.hh"
+#include "pdns/logger.hh"
+#include "pdns/arguments.hh"
+
+#include "soracle.hh"
+
+
+#include <sstream>
+
+gOracleBackend::gOracleBackend(const string &mode, const string &suffix)  : GSQLBackend(mode,suffix)
+{
+  try {
+    setDB(new SOracle(getArg("dbname"),
+                    getArg("host"),
+                    getArgAsNum("port"),
+                    getArg("socket"),
+                    getArg("user"),
+                    getArg("password")));
+    
+  }
+  
+  catch(SSqlException &e) {
+    L<<Logger::Error<<mode<<" Connection failed: "<<e.txtReason()<<endl;
+    throw AhuException("Unable to launch "+mode+" connection: "+e.txtReason());
+  }
+  L<<Logger::Warning<<mode<<" Connection succesful"<<endl;
+}
+
+class gOracleFactory : public BackendFactory
+{
+public:
+  gOracleFactory(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","Database backend user to connect as","powerdns");
+    declare(suffix,"host","Database backend host to connect to","");
+    declare(suffix,"port","Database backend port to connect to","0");
+    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 name 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'");
+    declare(suffix,"master-zone-query","Data", "select master from domains where name='%s' and type='SLAVE'");
+
+    declare(suffix,"info-zone-query","","select id,name,master,last_check,notified_serial,type from domains where name='%s'");
+
+    declare(suffix,"info-all-slaves-query","","select id,name,master,last_check,type from domains where type='SLAVE'");
+    declare(suffix,"supermaster-query","", "select account from supermasters where ip='%s' and nameserver='%s'");
+    declare(suffix,"insert-slave-query","", "insert into domains (id, type,name,master,account) values(domain_id_sequence.nextval, 'SLAVE','%s','%s','%s')");
+    declare(suffix,"insert-record-query","", "insert into records (id, content,ttl,prio,type,domain_id,name) values (records_id_sequence.nextval, '%s',%d,%d,'%s',%d,'%s')");
+    declare(suffix,"update-serial-query","", "update domains set notified_serial=%d where id=%d");
+    declare(suffix,"update-lastcheck-query","", "update domains set last_check=%d where id=%d");
+    declare(suffix,"info-all-master-query","", "select id,name,master,last_check,notified_serial,type from domains where type='MASTER'");
+    declare(suffix,"delete-zone-query","", "delete from records where domain_id=%d");
+
+
+  }
+  
+  DNSBackend *make(const string &suffix="")
+  {
+    return new gOracleBackend(d_mode,suffix);
+  }
+private:
+  const string d_mode;
+};
+
+
+//! Magic class that is activated when the dynamic library is loaded
+class gOracleLoader
+{
+public:
+  //! This reports us to the main UeberBackend class
+  gOracleLoader()
+  {
+    BackendMakers().report(new gOracleFactory("goracle"));
+    L<<Logger::Warning<<"This is module goraclebackend.so reporting"<<endl;
+  }
+};
+static gOracleLoader goracleloader;
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..157bc79d534f1cce9ae49724026a747ca949cef0 100644 (file)
@@ -0,0 +1,16 @@
+#include <string>
+#include <map>
+
+#include "pdns/backends/gsql/gsqlbackend.hh"
+
+using namespace std;
+
+/** The gOracleBackend is a DNSBackend that can answer DNS related questions. It looks up data
+    in PostgreSQL */
+class gOracleBackend : public GSQLBackend
+{
+public:
+  gOracleBackend(const string &mode, const string &suffix); //!< Makes our connection to the database. Throws an exception if it fails.
+private:
+};
index 5349c99b5c0f57f6d6e8990967f95a1ae7ae288a..2dd0f48c674d42c2463db9df3d2d591c77d98f76 100644 (file)
@@ -7,10 +7,37 @@
 #include "pdns/misc.hh"
 #include "pdns/logger.hh"
 #include "pdns/dns.hh"
+#include <regex.h>
 using namespace std;
 
 bool SOracle::s_dolog;
 
+
+string SOracle::getOracleError()
+{
+  string mReason = "ORA-UNKNOWN";
+
+  if (d_errorHandle != NULL) {
+    text  msg[512];
+    sb4   errcode = 0;
+    
+    memset((void *) msg, (int)'\0', (size_t)512);
+    
+    OCIErrorGet((dvoid *) d_errorHandle,1, NULL, &errcode, msg, sizeof(msg), OCI_HTYPE_ERROR);
+    if (errcode) {
+      char *p = (char*) msg;
+      while (*p++ != 0x00) {
+       if (*p == '\n' || *p == '\r') {
+         *p = ';';
+       }
+      }
+      
+      mReason = (char*) msg;
+    }
+  }
+  return mReason;
+}
+
 SOracle::SOracle(const string &database, const string &host, u_int16_t port, 
                 const string &msocket, const string &user, 
                 const string &password)
@@ -40,13 +67,11 @@ SOracle::SOracle(const string &database, const string &host, u_int16_t port,
    
    const char *username = user.c_str();
 
-
-
    err = OCILogon(d_environmentHandle, d_errorHandle, &d_serviceContextHandle, (OraText*) username, strlen(username),
                  (OraText*) password.c_str(),  strlen(password.c_str()), (OraText*) database.c_str(), strlen(database.c_str()));
    
    if (err) {
-     throw sPerrorException("oops"); // mErrorHandle);
+     throw sPerrorException("Loging in to Oracle gave error: " + getOracleError());
    }
 
 
@@ -59,7 +84,29 @@ void SOracle::setLog(bool state)
 
 SOracle::~SOracle()
 {
+  if(d_handle) {
+    OCIHandleFree(d_handle, OCI_HTYPE_STMT);
+    d_handle=0;
+  }
 
+  int err;
+  if (d_serviceContextHandle != NULL) {
+    err=OCILogoff(d_serviceContextHandle, d_errorHandle); 
+    if(err) {
+      cerr<<"Problems logging out: "+getOracleError()<<endl;
+    }
+  }
+   
+  if (d_errorHandle != NULL) {
+    OCIHandleFree(d_errorHandle, OCI_HTYPE_ERROR);
+    d_errorHandle = NULL;
+  }
+  
+  if (d_environmentHandle != NULL) {
+    OCIHandleFree(d_environmentHandle, OCI_HTYPE_ENV);
+    d_environmentHandle = NULL;
+  }
+  
 }
 
 SSqlException SOracle::sPerrorException(const string &reason)
@@ -72,8 +119,82 @@ int SOracle::doCommand(const string &query)
   return doQuery(query);
 }
 
+int getNumFields(const string& query)
+{
+  string lquery=toLower(query);
+  char* delim[]={" from ", "\tfrom\t", "\tfrom ", " from\t"};
+  int n=0;
+  string::size_type pos;
+  for(n=0; n < 4 && (pos=lquery.find(delim[n]))==string::npos; ++n)
+    ;
+
+  if(n==4)
+    return -1;
+
+  unsigned int num=1;
+
+  for(unsigned int n=0; n < pos; ++n)
+    if(lquery[n]==',')
+      num++;
+
+  return num;
+}
+
 int SOracle::doQuery(const string &query)
 {
+  if(query=="begin") // oracle does this implicitly
+    return 0;
+
+  int err = OCIHandleAlloc(d_environmentHandle, (dvoid **) &d_handle, OCI_HTYPE_STMT, 0, NULL);
+        
+  if (err) {
+    throw sPerrorException("Allocating a query handle: "+getOracleError());
+  }
+
+  err = OCIStmtPrepare(d_handle, d_errorHandle, (text*) query.c_str(), strlen(query.c_str()),
+                      OCI_NTV_SYNTAX, OCI_DEFAULT);
+  
+  if (err) {
+    throw sPerrorException("Preparing statement: "+getOracleError());
+  }
+
+  ub4 prefetch=1000;
+  err=OCIAttrSet(d_handle, (ub4) OCI_HTYPE_STMT,
+            (dvoid *) &prefetch, (ub4) sizeof(ub4), 
+            (ub4) OCI_ATTR_PREFETCH_ROWS, d_errorHandle);
+
+  if (err) {
+    throw sPerrorException("setting prefetch: "+getOracleError());
+  }
+
+
+  //  cerr<<"Done preparing '"<<query<<"'"<<endl;
+
+  d_numfields=getNumFields(query);
+
+  for(int n=0; n < d_numfields ; ++n) {
+    //    cerr<<"bind: "<<n<<endl;
+    OCIDefine *theDefineHandle = NULL; 
+    err = OCIDefineByPos(d_handle, &theDefineHandle, d_errorHandle, n+1, d_fields[n].content,
+                        sizeof(d_fields[n].content) - 1, SQLT_STR, (dvoid*) &d_fields[n].indicator, NULL, NULL, OCI_DEFAULT);
+    
+    if (err) {
+      throw sPerrorException("Error binding returns: "+getOracleError());
+    }
+  }
+
+  //  cerr<<"Done binding fields"<<endl;
+
+  d_queryResult = OCIStmtExecute(d_serviceContextHandle, d_handle, d_errorHandle, 1, 0,
+                                (OCISnapshot *)NULL, (OCISnapshot*) NULL, OCI_DEFAULT);
+  
+  if (d_queryResult != OCI_SUCCESS && d_queryResult != OCI_SUCCESS_WITH_INFO && d_queryResult != OCI_NO_DATA) {
+    throw sPerrorException("executing oracle query: "+getOracleError());
+  }
+
+  //  cerr<<"Done executing: "<<d_queryResult<<endl;
+
+
 
   return 0;
 }
@@ -94,7 +215,26 @@ bool SOracle::getRow(row_t &row)
 {
   row.clear();
 
-  return false;
+  if (d_queryResult == OCI_NO_DATA) {
+    OCIHandleFree(d_handle, OCI_HTYPE_STMT);
+    d_handle=0;
+    return false;
+  }
+  else {
+    for(int n=0;n < d_numfields ;++n)
+      if(!d_fields[n].indicator)
+       row.push_back(d_fields[n].content);
+      else
+       row.push_back("");
+  }
+
+  d_queryResult = OCIStmtFetch(d_handle, d_errorHandle, 1, 0, 0);
+  if (d_queryResult != OCI_SUCCESS && d_queryResult != OCI_SUCCESS_WITH_INFO && d_queryResult != OCI_NO_DATA) {
+    throw sPerrorException("fetching next row of oracle query: "+getOracleError());
+  }
+  
+
+  return true;
 }
 
 string SOracle::escape(const string &name)
@@ -102,35 +242,47 @@ string SOracle::escape(const string &name)
   string a;
 
   for(string::const_iterator i=name.begin();i!=name.end();++i) {
-    if(*i=='\'' || *i=='\\')
-      a+='\\';
+    if(*i=='\'')
+      a+='\'';
     a+=*i;
   }
   return a;
 }
 
+#if 0
 
-int main()
+int main(int argc, char **argv)
 {
+  for(int outer=0;outer<2; ++outer) {
   try {
-    SOracle s("kkfnetmail","127.0.0.1");
-    SSql::result_t juh;
-    
-    int num=s.doQuery("select *, from mboxes", juh);
-    cout<<num<<" responses"<<endl;
-    
-    for(int i=0;i<num;i++) {
-      const SSql::row_t &row=juh[i];
+    SOracle s(argv[1],"",0,"",argv[2],argv[3]);
 
-      for(SSql::row_t::const_iterator j=row.begin();j!=row.end();++j)
-       cout <<"'"<< *j<<"', ";
-      cout<<endl;
+    cerr<<"Ready to do queries"<<endl;
+    time_t then=time(0);
+    
+    int loops;
+    for(loops=0;loops < 6; ++loops) {
+      s.doQuery("select id, content from records");
+      
+      SSql::row_t row;
+      
+      while(s.getRow(row)) {
+       for(SSql::row_t::const_iterator j=row.begin();j!=row.end();++j)
+         cout <<"'"<< *j<<"', ";
+       cout<<"\n";
+      }
     }
+    time_t spent=time(0)-then;
+    if(spent)
+      cerr<<"Loops per second: "<< loops/spent<<endl;
+  }
+  catch(string &e) {
+    cerr<<"fatal: "<<e<<endl;
   }
   catch(SSqlException &e) {
     cerr<<e.txtReason()<<endl;
   }
+  }
 }
 
-
-
+#endif
index 886fc5aee2308aef4e25e27dab63639c6efe1785..a0ff5da208fa41a989bac12c5f4563eab0b2d729 100644 (file)
@@ -29,8 +29,20 @@ private:
   OCIError  *d_errorHandle;
   OCISvcCtx *d_serviceContextHandle;
   OCIStmt   *d_statementHandles[10];
-  
+
+  struct oresult {
+    char content[256];
+    sb2 indicator;
+  } d_fields[10];
+  OCIStmt* d_handle;
+
+  dsword d_queryResult;
+
+  string getOracleError();
+
   static bool s_dolog;
+  int d_numfields;
+  //  int getNumFields(const string& query);
 };
       
 #endif /* SSORACLE_HH */
index 9b6f3f63582baf4184d5301a2c2b03fa8497dfa0..6a125b32de5ecf36d89602b7ca4afba5232c2d07 100644 (file)
@@ -7363,6 +7363,58 @@ GRANT ALL ON records_id_seq TO pdns;
          feed it the schema above.
        </para>
       </sect2>
+      <sect2><title>Oracle specifics</title>
+      <para>
+       The default setup conforms to the following schema, which you should add to a PostgreSQL database.
+       <programlisting>
+
+create table domains (
+ id             NUMBER,
+ name           VARCHAR(255) NOT NULL,
+ master                 VARCHAR(20) DEFAULT NULL,
+ last_check     INT DEFAULT NULL,
+ type           VARCHAR(6) NOT NULL,
+ notified_serial INT DEFAULT NULL, 
+ account         VARCHAR(40) DEFAULT NULL,
+ primary key (id)
+);
+create sequence DOMAINS_ID_SEQUENCE; 
+create index DOMAINS$NAME on Domains (NAME);
+
+CREATE TABLE records (
+        id              number(11) not NULL,
+        domain_id       INT DEFAULT NULL REFERENCES Domains(ID) ON DELETE CASCADE,
+        name            VARCHAR(255) DEFAULT NULL,
+        type            VARCHAR(6) DEFAULT NULL,
+        content         VARCHAR(255) DEFAULT NULL,
+        ttl             INT DEFAULT NULL,
+        prio            INT DEFAULT NULL,
+        change_date     INT DEFAULT NULL, 
+       primary key (id)
+);
+
+create index RECORDS$NAME on RECORDS (NAME);
+create sequence RECORDS_ID_SEQUENCE;
+
+create table supermasters (
+         ip VARCHAR(25) NOT NULL, 
+         nameserver VARCHAR(255) NOT NULL, 
+         account VARCHAR(40) DEFAULT NULL
+);
+
+       </programlisting>
+      </para>
+      <para>
+       This schema contains all elements needed for master, slave and superslave operation. Depending on which features will be used, the 'GRANT' statements
+       can be trimmed to make sure PDNS cannot subvert the contents of your database.
+      </para>
+      <para>
+       Zone2sql with the --gpgsql flag also assumes this layout is in place.
+       </para>
+
+      </sect2>
+
       <sect2><title>Basic functionality</title>
        <para>
          4 queries are needed for regular lookups, 4 for 'fancy records' which are disabled by default and 1 is needed for zone transfers.
@@ -7763,7 +7815,7 @@ GRANT ALL ON records_id_seq TO pdns;
        </para>
     </sect1>
     
-    <sect1 id="oracle"><Title>Generic Oracle backend</title>
+    <sect1 id="oracle"><Title>Oracle backend</title>
       <para>
        <table>
          <title>Oracle backend capabilities</title>