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 )
-#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
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
+
+#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;
+#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:
+};
#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)
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());
}
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)
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;
}
{
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)
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
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 */
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.
</para>
</sect1>
- <sect1 id="oracle"><Title>Generic Oracle backend</title>
+ <sect1 id="oracle"><Title>Oracle backend</title>
<para>
<table>
<title>Oracle backend capabilities</title>