From: Bert Hubert Date: Sun, 15 Jan 2006 21:26:03 +0000 (+0000) Subject: add opendbx X-Git-Tag: pdns-2.9.20~29 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=790e7c1b49abeea42c4778c3ef2dd009af88e672;p=thirdparty%2Fpdns.git add opendbx speedup in dnswriter (CHECK!) add publish to auto-build git-svn-id: svn://svn.powerdns.com/pdns/trunk/pdns@559 d19b8d6e-7fed-0310-83ef-9ca221ded41b --- diff --git a/build-scripts/auto-build b/build-scripts/auto-build index fce76ba446..1aac1c04f2 100755 --- a/build-scripts/auto-build +++ b/build-scripts/auto-build @@ -23,6 +23,7 @@ fi rm -rf pdns svn co svn://svn.powerdns.com/pdns/trunk/pdns cd pdns +LATEST=$(svn info . | awk '/^Revision/ { print $2 }' ) DATE=$(date +%Y%m%d).$LATEST ssed -r "s/Version: (.*)/Version: \\1.$DATE/" -i *.spec ssed -r "s/AM_INIT_AUTOMAKE\\(pdns, (.*)\\)/AM_INIT_AUTOMAKE\(pdns, \\1.$DATE\)/" -i configure.in @@ -35,5 +36,7 @@ fakeroot ./build-scripts/rpm-build-instruction build-scripts/tar-gz-build-instruction cd .. mv pdns/*.tar.gz . +ssh adsl-xs4all.ds9a.nl mkdir /var/www/svn.powerdns.com/snapshots/$LATEST +scp pdns*$LATEST*{rpm,deb,tar.gz} adsl-xs4all.ds9a.nl:/var/www/svn.powerdns.com/snapshots/$LATEST diff --git a/configure.in b/configure.in index 445ac1f71f..93c79c0eae 100644 --- a/configure.in +++ b/configure.in @@ -412,7 +412,7 @@ AC_OUTPUT(Makefile modules/Makefile pdns/Makefile codedocs/Makefile \ pdns/backends/Makefile pdns/backends/bind/Makefile pdns/pdns pdns/precursor \ modules/mysqlbackend/Makefile modules/pdnsbackend/Makefile \ modules/gmysqlbackend/Makefile modules/db2backend/Makefile \ -modules/geobackend/Makefile \ +modules/geobackend/Makefile modules/opendbxbackend/Makefile \ modules/pipebackend/Makefile modules/oraclebackend/Makefile \ modules/xdbbackend/Makefile modules/odbcbackend/Makefile \ modules/gpgsqlbackend/Makefile modules/ldapbackend/Makefile diff --git a/debian-pdns/control b/debian-pdns/control index 7ecdf0123d..341a0bf9a9 100644 --- a/debian-pdns/control +++ b/debian-pdns/control @@ -51,6 +51,13 @@ Provides: pdns-backend Description: LDAP backend for pDNS This package contains a LDAP backend for the PowerDNS nameserver. +Package: pdns-backend-opendbx +Architecture: any +Depends: pdns (= ${dpkg:Version}), ${shlibs:Depends} +Provides: pdns-backend +Description: OpenDBX backend for pDNS + This package contains a OpenDBX backend for the PowerDNS nameserver. + Package: pdns-backend-mysql Architecture: any Depends: pdns (= ${dpkg:Version}), ${shlibs:Depends} diff --git a/debian-pdns/rules b/debian-pdns/rules index f274c2bf48..d960aa00e1 100755 --- a/debian-pdns/rules +++ b/debian-pdns/rules @@ -2,8 +2,8 @@ tmpdir := $(shell pwd)/debian-pdns/tmp be_tmpdir := $(shell pwd)/debian-pdns/tmp-backend -backends := ldap mysql pipe xdb gmysql gpgsql gsqlite -debs := ldap mysql pipe xdb pgsql sqlite +backends := opendbx ldap mysql pipe xdb gmysql gpgsql gsqlite +debs := opendbx ldap mysql pipe xdb pgsql sqlite binary-doc: -make -C pdns/docs html/index.html diff --git a/modules/opendbxbackend/Makefile.am b/modules/opendbxbackend/Makefile.am new file mode 100644 index 0000000000..66d48d258c --- /dev/null +++ b/modules/opendbxbackend/Makefile.am @@ -0,0 +1,5 @@ +lib_LTLIBRARIES = libopendbxbackend.la +libopendbxbackend_la_SOURCES = odbxbackend.hh odbxbackend.cc odbxprivate.cc +libopendbxbackend_la_LIBADD = -lopendbx + +EXTRA_DIST = OBJECTFILES OBJECTLIBS diff --git a/modules/opendbxbackend/OBJECTFILES b/modules/opendbxbackend/OBJECTFILES new file mode 100644 index 0000000000..cf716f8728 --- /dev/null +++ b/modules/opendbxbackend/OBJECTFILES @@ -0,0 +1 @@ +odbxbackend.o odbxprivate.o diff --git a/modules/opendbxbackend/OBJECTLIBS b/modules/opendbxbackend/OBJECTLIBS new file mode 100644 index 0000000000..d9fd8a6327 --- /dev/null +++ b/modules/opendbxbackend/OBJECTLIBS @@ -0,0 +1 @@ +-lopendbx \ No newline at end of file diff --git a/modules/opendbxbackend/odbxbackend.cc b/modules/opendbxbackend/odbxbackend.cc new file mode 100644 index 0000000000..c03008c6cb --- /dev/null +++ b/modules/opendbxbackend/odbxbackend.cc @@ -0,0 +1,614 @@ +#include "odbxbackend.hh" + + + +unsigned int odbx_host_index = 0; + + + +OdbxBackend::OdbxBackend( const string& suffix ) +{ + int err = -1; + unsigned int idx, i, h; + vector hosts; + + + try + { + m_result = NULL; + m_myname = "[OpendbxBackend]"; + m_default_ttl = arg().asNum( "default-ttl" ); + m_qlog = arg().mustDo( "query-logging" ); + + setArgPrefix( "opendbx" + suffix ); + stringtok( hosts, getArg( "host" ), ", " ); + + idx = odbx_host_index++ % hosts.size(); + + for( i = 0; i < hosts.size(); i++ ) + { + h = ( idx + i ) % hosts.size(); + if( !( err = odbx_init( &m_handle, getArg( "backend" ).c_str(), hosts[h].c_str(), getArg( "port" ).c_str() ) ) ) { break; } + } + + if( err < 0 ) + { + L.log( m_myname + " OdbxBackend: Unable to connect to server - " + string( odbx_error( m_handle, err ) ), Logger::Error ); + throw( AhuException( "Fatal: odbx_init() failed" ) ); + } + + if( ( err = odbx_bind_simple( m_handle, getArg( "database" ).c_str(), getArg( "username" ).c_str(), getArg( "password" ).c_str() ) ) < 0 ) + { + L.log( m_myname + " OdbxBackend: Unable to bind to database - " + string( odbx_error( m_handle, err ) ), Logger::Error ); + throw( AhuException( "Fatal: odbx_bind_simple() failed" ) ); + } + } + catch( exception& e ) + { + L.log( m_myname + " OdbxBackend: Caught STL exception - " + e.what(), Logger::Error ); + throw( DBException( "Fatal: STL exception" ) ); + } + + L.log( m_myname + " Connection succeeded", Logger::Notice ); +} + + + +OdbxBackend::~OdbxBackend() +{ + odbx_unbind( m_handle ); + odbx_finish( m_handle ); +} + + + +bool OdbxBackend::getDomainInfo( const string& domain, DomainInfo& di ) +{ + const char* tmp; + string stmt, type; + + + try + { + DLOG( L.log( m_myname + " getDomainInfo()", Logger::Debug ) ); + + stmt = strbind( ":name", escape( toLower( domain ) ), getArg( "sql-zoneinfo" ) ); + execStmt( stmt.c_str(), stmt.size(), true ); + + if( !getRecord() ) { return false; } + + do + { + di.id = 0; + di.zone = ""; + di.master = ""; + di.last_check = 0; + di.backend = this; + type = ""; + + if( ( tmp = odbx_field_value( m_result, 0 ) ) != NULL ) + { + di.id = strtol( tmp, NULL, 10 ); + } + + if( ( tmp = odbx_field_value( m_result, 1 ) ) != NULL ) + { + di.zone = string( tmp ); + } + + if( ( tmp = odbx_field_value( m_result, 2 ) ) != NULL ) + { + di.master = string( tmp ); + } + + if( ( tmp = odbx_field_value( m_result, 3 ) ) != NULL ) + { + di.last_check = strtol( tmp, NULL, 10 ); + } + + if( ( tmp = odbx_field_value( m_result, 5 ) ) != NULL ) + { + type = string( tmp ); + } + + di.kind = DomainInfo::Native; + + if( type == "SLAVE" ) + { + SOAData sd; + + sd.serial = 0; + + if( ( tmp = odbx_field_value( m_result, 6 ) ) != NULL ) + { + DNSPacket::fillSOAData( string( tmp ), sd ); + } + else + { + L.log( m_myname + " getDomainInfo: No serial for '" + domain + "' found", Logger::Notice ); + } + + di.serial = sd.serial; + di.kind = DomainInfo::Slave; + } + else if( type == "MASTER" ) + { + di.kind = DomainInfo::Master; + } + } + while( getRecord() ); + } + catch( exception& e ) + { + L.log( m_myname + " getDomainInfo: Caught STL exception - " + e.what(), Logger::Error ); + throw( DBException( "Error: STL exception" ) ); + } + + return true; +} + + + +bool OdbxBackend::list( const string& target, int zoneid ) +{ + string stmt; + size_t len; + + + + try + { + DLOG( L.log( m_myname + " list()", Logger::Debug ) ); + + m_qname = ""; + m_result = NULL; + + len = snprintf( m_buffer, sizeof( m_buffer ) - 1, "%d", zoneid ); + + if( len < 0 || len > sizeof( m_buffer ) - 1 ) + { + L.log( m_myname + " list: Unable to convert zone id to string", Logger::Error ); + throw( DBException( "Error: Libc error" ) ); + } + + stmt = strbind( ":id", string( m_buffer, len ), getArg( "sql-list" ) ); + + execStmt( stmt.c_str(), stmt.size(), true ); + } + catch( exception& e ) + { + L.log( m_myname + " list: Caught STL exception - " + e.what(), Logger::Error ); + throw( DBException( "Error: STL exception" ) ); + } + + return true; +} + + + +void OdbxBackend::lookup( const QType& qtype, const string& qname, DNSPacket* dnspkt, int zoneid ) +{ + string stmt; + + + try + { + DLOG( L.log( m_myname + " lookup()", Logger::Debug ) ); + + m_result = NULL; + m_qname = qname; + + if( zoneid < 0 ) + { + if( qtype.getCode() == QType::ANY ) + { + stmt = getArg( "sql-lookup" ); + } else { + stmt = strbind( ":type", qtype.getName(), getArg( "sql-lookuptype" ) ); + } + } + else + { + if( qtype.getCode() == QType::ANY ) + { + stmt = getArg( "sql-lookupid" ); + } else { + stmt = strbind( ":type", qtype.getName(), getArg( "sql-lookuptypeid" ) ); + } + + size_t len = snprintf( m_buffer, sizeof( m_buffer ) - 1, "%d", zoneid ); + + if( len < 0 || len > sizeof( m_buffer ) - 1 ) + { + L.log( m_myname + " lookup: Unable to convert zone id to string", Logger::Error ); + throw( DBException( "Error: Libc error" ) ); + } + + stmt = strbind( ":id", string( m_buffer, len ), stmt ); + } + + stmt = strbind( ":name", escape( toLower( qname ) ), stmt ); + execStmt( stmt.c_str(), stmt.size(), true ); + } + catch( exception& e ) + { + L.log( m_myname + " lookup: Caught STL exception - " + e.what(), Logger::Error ); + throw( DBException( "Error: STL exception" ) ); + } +} + + + +bool OdbxBackend::get( DNSResourceRecord& rr ) +{ + const char* tmp; + + + try + { + DLOG( L.log( m_myname + " get()", Logger::Debug ) ); + + if( getRecord() ) + { + rr.content = ""; + rr.priority = 0; + rr.domain_id = 0; + rr.last_modified = 0; + rr.ttl = m_default_ttl; + rr.qname = m_qname; + + if( ( tmp = odbx_field_value( m_result, 0 ) ) != NULL ) + { + rr.content = string( tmp ); + } + + if( ( tmp = odbx_field_value( m_result, 1 ) ) != NULL ) + { + rr.ttl = strtoul( tmp, NULL, 10 ); + } + + if( ( tmp = odbx_field_value( m_result, 2 ) ) != NULL ) + { + rr.priority = (u_int16_t) strtoul( tmp, NULL, 10 ); + } + + if( ( tmp = odbx_field_value( m_result, 3 ) ) != NULL ) + { + rr.qtype = QType( tmp ); + } + + if( ( tmp = odbx_field_value( m_result, 4 ) ) != NULL ) + { + rr.domain_id = strtol( tmp, NULL, 10 ); + } + + if( m_qname.empty() && ( tmp = odbx_field_value( m_result, 5 ) ) != NULL ) + { + rr.qname = string( tmp ); + } + + return true; + } + } + catch( exception& e ) + { + L.log( m_myname + " get: Caught STL exception - " + e.what(), Logger::Error ); + throw( DBException( "Error: STL exception" ) ); + } + + return false; +} + + +void OdbxBackend::setFresh( u_int32_t domain_id ) +{ + size_t len; + + + try + { + DLOG( L.log( m_myname + " setFresh()", Logger::Debug ) ); + + len = snprintf( m_buffer, sizeof( m_buffer ) - 1, getArg( "sql-update-lastcheck" ).c_str(), time( 0 ), domain_id ); + + if( len < 0 || len > sizeof( m_buffer ) - 1 ) + { + L.log( m_myname + " setFresh: Unable to insert values into statement '" + getArg( "sql-update-lastcheck" ) + "'", Logger::Error ); + throw( DBException( "Error: Libc error" ) ); + } + + execStmt( m_buffer, len, false ); + } + catch ( exception& e ) + { + L.log( m_myname + " setFresh: Caught STL exception - " + e.what(), Logger::Error ); + throw( DBException( "Error: STL exception" ) ); + } +} + + + +void OdbxBackend::setNotified( u_int32_t domain_id, u_int32_t serial ) +{ + size_t len; + + + try + { + DLOG( L.log( m_myname + " setNotified()", Logger::Debug ) ); + + len = snprintf( m_buffer, sizeof( m_buffer ) - 1, getArg( "sql-update-serial" ).c_str(), serial, domain_id ); + + if( len < 0 || len > sizeof( m_buffer ) - 1 ) + { + L.log( m_myname + " setNotified: Unable to insert values into statement '" + getArg( "sql-update-serial" ) + "'", Logger::Error ); + throw( DBException( "Error: Libc error" ) ); + } + + execStmt( m_buffer, len, false ); + } + catch ( exception& e ) + { + L.log( m_myname + " setNotified: Caught STL exception - " + e.what(), Logger::Error ); + throw( DBException( "Error: STL exception" ) ); + } +} + + + +bool OdbxBackend::isMaster( const string& domain, const string& ip ) +{ + string stmt; + + + try + { + DLOG( L.log( m_myname + " isMaster()", Logger::Debug ) ); + + stmt = strbind( ":name", escape( toLower( domain ) ), getArg( "sql-master" ) ); + execStmt( stmt.c_str(), stmt.size(), true ); + + if( !getRecord() ) { return false; } + + do + { + if( odbx_field_value( m_result, 0 ) != NULL ) + { + if( !strcmp( odbx_field_value( m_result, 0 ), ip.c_str() ) ) + { + return true; + } + } + } + while( getRecord() ); + } + catch ( exception& e ) + { + L.log( m_myname + " isMaster: Caught STL exception - " + e.what(), Logger::Error ); + throw( DBException( "Error: STL exception" ) ); + } + + return false; +} + + + +void OdbxBackend::getUnfreshSlaveInfos( vector* unfresh ) +{ + try + { + DLOG( L.log( m_myname + " getUnfreshSlaveInfos()", Logger::Debug ) ); + + if( unfresh != NULL ) + { + getDomainList( getArg( "sql-infoslaves" ), unfresh, &checkSlave ); + } + } + catch ( exception& e ) + { + L.log( m_myname + " getUnfreshSlaveInfo: Caught STL exception - " + e.what(), Logger::Error ); + throw( DBException( "Error: STL exception" ) ); + } +} + + + +void OdbxBackend::getUpdatedMasters( vector* updated ) +{ + try + { + DLOG( L.log( m_myname + " getUpdatedMasters()", Logger::Debug ) ); + + if( updated != NULL ) + { + getDomainList( getArg( "sql-infomasters" ), updated, &checkMaster ); + } + } + catch ( exception& e ) + { + L.log( m_myname + " getUpdatedMasters: Caught STL exception - " + e.what(), Logger::Error ); + throw( DBException( "Error: STL exception" ) ); + } +} + + + +bool OdbxBackend::superMasterBackend( const string& ip, const string& domain, const vector& set, string* account, DNSBackend** ddb ) +{ + string stmt; + vector::const_iterator i; + + + try + { + DLOG( L.log( m_myname + " superMasterBackend()", Logger::Debug ) ); + + if( account != NULL && ddb != NULL ) + { + for( i = set.begin(); i != set.end(); i++ ) + { + stmt = strbind( ":ip", escape( ip ), getArg( "sql-supermaster" ) ); + stmt = strbind( ":ns", escape( i->content ), stmt ); + + execStmt( stmt.c_str(), stmt.size(), true ); + + if( !getRecord() ) { return false; } + + do + { + if( odbx_field_value( m_result, 0 ) != NULL ) + { + *account = string( odbx_field_value( m_result, 0 ), odbx_field_length( m_result, 0 ) ); + } + } + while( getRecord() ); + + *ddb=this; + return true; + } + } + } + catch ( exception& e ) + { + L.log( m_myname + " superMasterBackend: Caught STL exception - " + e.what(), Logger::Error ); + throw( DBException( "Error: STL exception" ) ); + } + + return false; +} + + + +bool OdbxBackend::createSlaveDomain( const string& ip, const string& domain, const string& account ) +{ + size_t len; + + + try + { + DLOG( L.log( m_myname + " createSlaveDomain()", Logger::Debug ) ); + + len = snprintf( m_buffer, sizeof( m_buffer ) - 1, getArg( "sql-insert-slave" ).c_str(), escape( toLower( domain ) ).c_str(), + escape( ip ).c_str(), escape( account ).c_str() ); + + if( len < 0 || len > sizeof( m_buffer ) - 1 ) + { + L.log( m_myname + " createSlaveDomain: Unable to insert values in statement '" + getArg( "sql-insert-slave" ) + "'", Logger::Error ); + throw( DBException( "Error: Libc error" ) ); + } + + execStmt( m_buffer, len, false ); + } + catch ( exception& e ) + { + L.log( m_myname + " createSlaveDomain: Caught STL exception - " + e.what(), Logger::Error ); + throw( DBException( "Error: STL exception" ) ); + } + + return true; +} + + + +bool OdbxBackend::feedRecord( const DNSResourceRecord& rr ) +{ + size_t len; + + + try + { + DLOG( L.log( m_myname + " feedRecord()", Logger::Debug ) ); + + len = snprintf( m_buffer, sizeof( m_buffer ) - 1, getArg( "sql-insert-record" ).c_str(), rr.domain_id, + escape( toLower( rr.qname ) ).c_str(), rr.qtype.getName().c_str(), rr.ttl, rr.priority, escape( rr.content ).c_str() ); + + if( len < 0 || len > sizeof( m_buffer ) - 1 ) + { + L.log( m_myname + " feedRecord: Unable to insert values in statement '" + getArg( "sql-insert-record" ) + "'", Logger::Error ); + throw( DBException( "Error: Libc error" ) ); + } + + execStmt( m_buffer, len, false ); + } + catch ( exception& e ) + { + L.log( m_myname + " feedRecord: Caught STL exception - " + e.what(), Logger::Error ); + throw( DBException( "Error: STL exception" ) ); + } + + return true; +} + + + +bool OdbxBackend::startTransaction( const string& domain, int zoneid ) +{ + size_t len; + string stmt; + + + try + { + DLOG( L.log( m_myname + " startTransaction()", Logger::Debug ) ); + + stmt = getArg( "sql-transactbegin" ); + execStmt( stmt.c_str(), stmt.size(), false ); + + len = snprintf( m_buffer, sizeof( m_buffer ) - 1, "%d", zoneid ); + + if( len < 0 || len > sizeof( m_buffer ) - 1 ) + { + L.log( m_myname + " lookup: Unable to convert zone id to string", Logger::Error ); + throw( DBException( "Error: Libc error" ) ); + } + + stmt = strbind( ":id", string( m_buffer, len ), getArg( "sql-zonedelete" ) ); + + execStmt( stmt.c_str(), stmt.size(), false ); + } + catch ( exception& e ) + { + L.log( m_myname + " startTransaction: Caught STL exception - " + e.what(), Logger::Error ); + throw( DBException( "Error: STL exception" ) ); + } + + return true; +} + + + +bool OdbxBackend::commitTransaction() +{ + try + { + DLOG( L.log( m_myname + " commitTransaction()", Logger::Debug ) ); + + execStmt( getArg( "sql-transactend" ).c_str(), getArg( "sql-transactend" ).size(), false ); + } + catch ( exception& e ) + { + L.log( m_myname + " commitTransaction: Caught STL exception - " + e.what(), Logger::Error ); + throw( DBException( "Error: STL exception" ) ); + } + + return true; +} + + + +bool OdbxBackend::abortTransaction() +{ + try + { + DLOG( L.log( m_myname + " abortTransaction()", Logger::Debug ) ); + + execStmt( getArg( "sql-transactabort" ).c_str(), getArg( "sql-transabort" ).size(), false ); + } + catch ( exception& e ) + { + L.log( m_myname + " abortTransaction: Caught STL exception - " + e.what(), Logger::Error ); + throw( DBException( "Error: STL exception" ) ); + } + + return true; +} diff --git a/modules/opendbxbackend/odbxbackend.hh b/modules/opendbxbackend/odbxbackend.hh new file mode 100644 index 0000000000..798b515191 --- /dev/null +++ b/modules/opendbxbackend/odbxbackend.hh @@ -0,0 +1,169 @@ +/* + * PowerDNS OpenDBX Backend + * Copyright (C) 2005 Norbert Sendetzky + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "modules/ldapbackend/utils.hh" + + +#ifndef ODBXBACKEND_HH +#define ODBXBACKEND_HH + + +#define BUFLEN 512 + + +using std::string; +using std::vector; + + + +bool checkSlave( u_int32_t last, u_int32_t notified, SOAData* sd, DomainInfo* di ); +bool checkMaster( u_int32_t last, u_int32_t notified, SOAData* sd, DomainInfo* di ); + + +class OdbxBackend : public DNSBackend +{ + string m_myname; + string m_qname; + int m_default_ttl; + bool m_qlog; + odbx_t* m_handle; + odbx_result_t* m_result; + char m_escbuf[BUFLEN]; + char m_buffer[2*BUFLEN]; + + bool getRecord(); + void execStmt( const char* stmt, unsigned long length, bool select ); + void getDomainList( const string& query, vector* list, bool (*check_fcn)(u_int32_t,u_int32_t,SOAData*,DomainInfo*) ); + string escape( const string& str ); + + +public: + + OdbxBackend( const string& suffix="" ); + ~OdbxBackend(); + + void lookup( const QType& qtype, const string& qdomain, DNSPacket* p = 0, int zoneid = -1 ); + bool list( const string& target, int domain_id ); + bool get( DNSResourceRecord& rr ); + + bool startTransaction( const string& domain, int domain_id ); + bool commitTransaction(); + bool abortTransaction(); + + bool isMaster( const string& domain, const string& ip ); + bool getDomainInfo( const string& domain, DomainInfo& di ); + bool feedRecord( const DNSResourceRecord& rr ); + bool createSlaveDomain( const string& ip, const string& domain, const string& account ); + bool superMasterBackend( const string& ip, const string& domain, const vector& nsset, string* account, DNSBackend** ddb ); + + void getUpdatedMasters( vector* updated ); + void getUnfreshSlaveInfos( vector* unfresh ); + + void setFresh( u_int32_t domain_id ); + void setNotified( u_int32_t domain_id, u_int32_t serial ); +}; + + + +class OdbxFactory : public BackendFactory +{ + +public: + + OdbxFactory() : BackendFactory( "opendbx" ) {} + + + void declareArguments( const string &suffix="" ) + { + declare( suffix, "backend", "OpenDBX backend","mysql" ); + declare( suffix, "host", "Name or address of one or more DBMS server","127.0.0.1" ); + declare( suffix, "port", "Port the DBMS server is listening to","" ); + declare( suffix, "database", "Database name containing the DNS records","powerdns" ); + declare( suffix, "username","User for connecting to the DBMS","powerdns"); + declare( suffix, "password","Password for connecting to the DBMS",""); + + declare( suffix, "sql-list", "AXFR query", "SELECT content, ttl, prio, type, domain_id, name FROM records WHERE domain_id=':id'" ); + + declare( suffix, "sql-lookup", "Lookup query","SELECT content, ttl, prio, type, domain_id, name FROM records WHERE name=':name'" ); + declare( suffix, "sql-lookupid", "Lookup query with id","SELECT content, ttl, prio, type, domain_id, name FROM records WHERE name=':name' AND domain_id=':id'" ); + declare( suffix, "sql-lookuptype", "Lookup query with type","SELECT content, ttl, prio, type, domain_id, name FROM records WHERE type=':type' AND name=':name'" ); + declare( suffix, "sql-lookuptypeid", "Lookup query with type and id","SELECT content, ttl, prio, type, domain_id, name FROM records WHERE type=':type' AND name=':name' AND domain_id=':id'" ); + + declare( suffix, "sql-zonedelete","Delete all records for this zone","DELETE FROM records WHERE domain_id=':id'" ); + declare( suffix, "sql-zoneinfo","Get domain info","SELECT d.id, d.name, d.master, d.last_check, d.notified_serial, d.type, r.content FROM domains AS d LEFT JOIN records AS r ON d.id=r.domain_id WHERE ( d.name=':name' AND r.type='SOA' ) OR ( d.name=':name' AND r.domain_id IS NULL )" ); + + declare( suffix, "sql-transactbegin", "Start transaction", "BEGIN" ); + declare( suffix, "sql-transactend", "Finish transaction", "COMMIT" ); + declare( suffix, "sql-transactabort", "Abort transaction", "ROLLBACK" ); + + declare( suffix, "sql-insert-slave","Add slave domain", "INSERT INTO domains ( type, name, master, account ) VALUES ( 'SLAVE', '%s', '%s', '%s' )" ); + declare( suffix, "sql-insert-record","Feed record into table", "INSERT INTO records ( domain_id, name, type, ttl, prio, content ) VALUES ( '%d', '%s', '%s', '%d', '%d', '%s' )" ); + + declare( suffix, "sql-update-serial", "Set zone to notified", "UPDATE domains SET notified_serial='%d' WHERE id='%d'" ); + declare( suffix, "sql-update-lastcheck", "Set time of last check", "UPDATE domains SET last_check='%d' WHERE id='%d'" ); + + declare( suffix, "sql-master", "Get master record for zone", "SELECT master FROM domains WHERE type='SLAVE' AND name=':name'" ); + declare( suffix, "sql-supermaster","Get supermaster info", "SELECT account FROM supermasters WHERE ip=':ip' AND nameserver=':ns'" ); + + declare( suffix, "sql-infoslaves", "Get all unfresh slaves", "SELECT d.id, d.name, d.master, d.last_check, d.notified_serial, r.change_date, r.content FROM domains AS d LEFT JOIN records AS r ON d.id=r.domain_id WHERE ( d.type='SLAVE' AND r.type='SOA' ) OR ( d.type='SLAVE' AND r.domain_id IS NULL )" ); + declare( suffix, "sql-infomasters", "Get all updated masters", "SELECT d.id, d.name, d.master, d.last_check, d.notified_serial, r.change_date, r.content FROM domains AS d, records AS r WHERE d.type='MASTER' AND d.id=r.domain_id AND r.type='SOA'" ); + } + + + DNSBackend* make( const string &suffix="" ) + { + return new OdbxBackend( suffix ); + } +}; + + +class OdbxLoader +{ + OdbxFactory factory; + +public: + + OdbxLoader() + { + BackendMakers().report( &factory ); + L.log( " [OpendbxBackend] This is the opendbx module version "VERSION" ("__DATE__", "__TIME__") reporting", Logger::Info ); + } +}; + + +static OdbxLoader odbxloader; + + + +#endif /* ODBXBACKEND_HH */ diff --git a/modules/opendbxbackend/odbxprivate.cc b/modules/opendbxbackend/odbxprivate.cc new file mode 100644 index 0000000000..58a9d4ba13 --- /dev/null +++ b/modules/opendbxbackend/odbxprivate.cc @@ -0,0 +1,201 @@ +#include "odbxbackend.hh" + + + +void OdbxBackend::execStmt( const char* stmt, unsigned long length, bool select ) +{ + int err; + + + DLOG( L.log( m_myname + " execStmt()", Logger::Debug ) ); + + if( m_qlog ) { L.log( m_myname + " Query: " + stmt, Logger::Info ); } + + if( ( err = odbx_query( m_handle, stmt, length ) ) < 0 ) + { + L.log( m_myname + " execStmt: Unable to execute query - " + string( odbx_error( m_handle, err ) ), Logger::Error ); + throw( AhuException( "Error: odbx_query() failed" ) ); + } + + if( !select ) { while( getRecord() ); } +} + + + +bool OdbxBackend::getRecord() +{ + int err = 3; + + + DLOG( L.log( m_myname + " getRecord()", Logger::Debug ) ); + + do + { + if( m_result != NULL ) + { + if( err == 3 ) + { + if( ( err = odbx_row_fetch( m_result ) ) < 0 ) + { + L.log( m_myname + " getRecord: Unable to get next row - " + string( odbx_error( m_handle, err ) ), Logger::Error ); + throw( AhuException( "Error: odbx_row_fetch() failed" ) ); + } + + if( err > 0 ) + { +#ifdef VERBOSELOG + unsigned int i; + string fields; + + for( i = 0; i < odbx_column_count( m_result ); i++ ) + { + fields += string( odbx_column_name( m_result, i ) ); + + if( odbx_field_value( m_result, i ) != NULL ) + { + fields += "=" + string( odbx_field_value( m_result, i ) ) + ", "; + } + else + { + fields += "=NULL, "; + } + } + + L.log( m_myname + " Values: " + fields, Logger::Error ); +#endif + return true; + } + + } + + odbx_result_free( m_result ); + m_result = NULL; + } + } + while( ( err = odbx_result( m_handle, &m_result, NULL, 0 ) ) > 0 ); + + if( err < 0 ) + { + L.log( m_myname + " getRecord: Unable to get next result - " + string( odbx_error( m_handle, err ) ), Logger::Error ); + throw( AhuException( "Error: odbx_result() failed" ) ); + } + + m_result = NULL; + return false; +} + + + +string OdbxBackend::escape( const string& str ) +{ + int err; + unsigned long len = sizeof( m_escbuf ); + + + DLOG( L.log( m_myname + " escape()", Logger::Debug ) ); + + if( ( err = odbx_escape( m_handle, str.c_str(), str.size(), m_escbuf, &len ) ) < 0 ) + { + L.log( m_myname + " escape: Unable to escape string - " + string( odbx_error( m_handle, err ) ), Logger::Error ); + throw( AhuException( "Error: odbx_escape() failed" ) ); + } + + return string( m_escbuf, len ); +} + + + +void OdbxBackend::getDomainList( const string& stmt, vector* list, bool (*check_fcn)(u_int32_t,u_int32_t,SOAData*,DomainInfo*) ) +{ + const char* tmp; + u_int32_t nlast, nserial; + DomainInfo di; + SOAData sd; + + + DLOG( L.log( m_myname + " getDomainList()", Logger::Debug ) ); + + execStmt( stmt.c_str(), stmt.size(), true ); + + if( !getRecord() ) { return; } + + do + { + nlast = 0; + nserial = 0; + sd.serial = 0; + sd.refresh = 0; + + if( ( tmp = odbx_field_value( m_result, 6 ) ) != NULL ) + { + DNSPacket::fillSOAData( string( tmp ), sd ); + } + + if( !sd.serial && ( tmp = odbx_field_value( m_result, 5 ) ) != NULL ) + { + sd.serial = strtol( tmp, NULL, 10 ); + } + + if( ( tmp = odbx_field_value( m_result, 4 ) ) != NULL ) + { + nserial = strtol( tmp, NULL, 10 ); + } + + if( ( tmp = odbx_field_value( m_result, 3 ) ) != NULL ) + { + nlast = strtol( tmp, NULL, 10 ); + } + + if( (*check_fcn)( nlast, nserial, &sd, &di ) ) + { + if( ( tmp = odbx_field_value( m_result, 2 ) ) != NULL ) + { + di.master = string( tmp, odbx_field_length( m_result, 2 ) ); + } + + if( ( tmp = odbx_field_value( m_result, 1 ) ) != NULL ) + { + di.zone = string( tmp, odbx_field_length( m_result, 1 ) ); + } + + if( ( tmp = odbx_field_value( m_result, 0 ) ) != NULL ) + { + di.id = strtol( tmp, NULL, 10 ); + } + + di.last_check = nlast; + di.notified_serial = nserial; + di.serial = sd.serial; + di.backend = this; + + list->push_back( di ); + } + } + while( getRecord() ); +} + + + +bool checkSlave( u_int32_t nlast, u_int32_t nserial, SOAData* sd, DomainInfo* di ) +{ + if( nlast + sd->refresh < (u_int32_t) time( 0 ) ) + { + di->kind = DomainInfo::Slave; + return true; + } + + return false; +} + + + +bool checkMaster( u_int32_t nlast, u_int32_t nserial, SOAData* sd, DomainInfo* di ) +{ + if( nserial != sd->serial ) + { + di->kind = DomainInfo::Master; + return true; + } + + return false; +} diff --git a/modules/opendbxbackend/tables-mysql.sql b/modules/opendbxbackend/tables-mysql.sql new file mode 100644 index 0000000000..fbddd7e835 --- /dev/null +++ b/modules/opendbxbackend/tables-mysql.sql @@ -0,0 +1,48 @@ +CREATE TABLE domains ( + id INT auto_increment, + type VARCHAR(6) NOT NULL, + name VARCHAR(255) NOT NULL, + master VARCHAR(40) DEFAULT NULL, + account VARCHAR(40) DEFAULT NULL, + notified_serial INT DEFAULT NULL, + last_check INT DEFAULT NULL, +CONSTRAINT pk_id + PRIMARY KEY (id), +CONSTRAINT unq_name + UNIQUE (name) +) type=InnoDB; + + +CREATE TABLE records ( + id INT auto_increment, + domain_id INT DEFAULT NULL, + name VARCHAR(255) DEFAULT NULL, + type VARCHAR(6) DEFAULT NULL, + ttl INT DEFAULT NULL, + prio INT DEFAULT NULL, + content VARCHAR(255) DEFAULT NULL, + change_date INT DEFAULT NULL, +CONSTRAINT pk_id + PRIMARY KEY (id), +CONSTRAINT fk_domainid + FOREIGN KEY (domain_id) + REFERENCES domains(id) + ON UPDATE CASCADE + ON DELETE CASCADE +) type=InnoDB; + +CREATE INDEX idx_rname ON records(name); +CREATE INDEX idx_rname_rtype ON records(name,type); +CREATE INDEX idx_rdomainid ON records(domain_id); + + +CREATE TABLE supermasters ( + ip VARCHAR(40) NOT NULL, + nameserver VARCHAR(255) NOT NULL, + account VARCHAR(40) DEFAULT NULL +); + + +GRANT SELECT ON supermasters TO powerdns; +GRANT ALL ON domains TO powerdns; +GRANT ALL ON records TO powerdns; diff --git a/modules/opendbxbackend/tables-pgsql.sql b/modules/opendbxbackend/tables-pgsql.sql new file mode 100644 index 0000000000..5c62e7da6a --- /dev/null +++ b/modules/opendbxbackend/tables-pgsql.sql @@ -0,0 +1,50 @@ +CREATE TABLE domains ( + id SERIAL, + type VARCHAR(6) NOT NULL, + name VARCHAR(255) NOT NULL, + master VARCHAR(40) DEFAULT NULL, + account VARCHAR(40) DEFAULT NULL + notified_serial INT DEFAULT NULL, + last_check INT DEFAULT NULL, +CONSTRAINT pk_id + PRIMARY KEY (id), +CONSTRAINT unq_name + UNIQUE (name) +); + + +CREATE TABLE records ( + id SERIAL, + domain_id INT DEFAULT NULL, + name VARCHAR(255) DEFAULT NULL, + type VARCHAR(6) DEFAULT NULL, + ttl INT DEFAULT NULL, + prio INT DEFAULT NULL, + content VARCHAR(255) DEFAULT NULL, + change_date INT DEFAULT NULL, +CONSTRAINT pk_id + PRIMARY KEY (id), +CONSTRAINT fk_domainid + FOREIGN KEY (domain_id) + REFERENCES domains(id) + ON UPDATE CASCADE + ON DELETE CASCADE +); + +CREATE INDEX idx_rname ON records(name); +CREATE INDEX idx_rname_rtype ON records(name,type); +CREATE INDEX idx_rdomainid ON records(domain_id); + + +CREATE TABLE supermasters ( + ip VARCHAR(40) NOT NULL, + nameserver VARCHAR(255) NOT NULL, + account VARCHAR(40) DEFAULT NULL +); + + +GRANT SELECT ON supermasters TO powerdns; +GRANT ALL ON domains TO powerdns; +GRANT ALL ON domains_id_seq TO powerdns; +GRANT ALL ON records TO powerdns; +GRANT ALL ON records_id_seq TO powerdns; diff --git a/modules/opendbxbackend/tables-sqlite.sql b/modules/opendbxbackend/tables-sqlite.sql new file mode 100644 index 0000000000..ec3052fdf5 --- /dev/null +++ b/modules/opendbxbackend/tables-sqlite.sql @@ -0,0 +1,43 @@ +CREATE TABLE domains ( + id INT AUTOINCREMENT, + type VARCHAR(6) NOT NULL, + name VARCHAR(255) NOT NULL, + master VARCHAR(40) DEFAULT NULL, + account VARCHAR(40) DEFAULT NULL, + notified_serial INT DEFAULT NULL, + last_check INT DEFAULT NULL, +CONSTRAINT pk_id + PRIMARY KEY (id), +CONSTRAINT unq_name + UNIQUE (name) +); + + +CREATE TABLE records ( + id INT AUTOINCREMENT, + domain_id INT DEFAULT NULL, + name VARCHAR(255) DEFAULT NULL, + type VARCHAR(6) DEFAULT NULL, + ttl INT DEFAULT NULL, + prio INT DEFAULT NULL, + content VARCHAR(255) DEFAULT NULL, + change_date INT DEFAULT NULL, +CONSTRAINT pk_id + PRIMARY KEY (id), +CONSTRAINT fk_domainid + FOREIGN KEY (domain_id) + REFERENCES domains(id) + ON UPDATE CASCADE + ON DELETE CASCADE +); + +CREATE INDEX idx_rname ON records(name); +CREATE INDEX idx_rname_rtype ON records(name,type); +CREATE INDEX idx_rdomainid ON records(domain_id); + + +CREATE TABLE supermasters ( + ip VARCHAR(40) NOT NULL, + nameserver VARCHAR(255) NOT NULL, + account VARCHAR(40) DEFAULT NULL +); diff --git a/pdns/dnswriter.cc b/pdns/dnswriter.cc index feb1562ac0..4b79120ea7 100644 --- a/pdns/dnswriter.cc +++ b/pdns/dnswriter.cc @@ -13,10 +13,25 @@ DNSPacketWriter::DNSPacketWriter(vector& content, const string& qname, dnsheader.qdcount=htons(1); const uint8_t* ptr=(const uint8_t*)&dnsheader; - d_content.insert(d_content.end(), ptr, ptr + sizeof(dnsheader)); + uint32_t len=d_content.size(); + d_content.resize(len + sizeof(dnsheader)); + uint8_t* dptr=(&*d_content.begin()) + len; + + + memcpy(dptr, ptr, sizeof(dnsheader)); + + // d_content.insert(d_content.end(), ptr, ptr + sizeof(dnsheader)); xfrLabel(qname, false); - d_content.insert(d_content.end(), d_record.begin(), d_record.end()); + + len=d_content.size(); + d_content.resize(len + d_record.size()); + ptr=&*d_record.begin(); + dptr=(&*d_content.begin()) + len; + + memcpy(dptr, ptr, d_record.size()); + // d_content.insert(d_content.end(), d_record.begin(), d_record.end()); + d_record.clear(); qtype=htons(qtype);