diff -Nur pdns-2.9.21.1.orig/modules/ldapbackend/ldapbackend.cc pdns-2.9.21.1.int16/modules/ldapbackend/ldapbackend.cc --- pdns-2.9.21.1.orig/modules/ldapbackend/ldapbackend.cc 2007-04-21 07:56:36.000000000 -0600 +++ pdns-2.9.21.1.int16/modules/ldapbackend/ldapbackend.cc 2008-10-06 10:08:55.000000000 -0600 @@ -433,7 +433,7 @@ continue; } - rr.priority = (uint16_t) strtoul( (content.substr( 0, first )).c_str(), &endptr, 10 ); + rr.priority = (u_int16_t) strtoul( (content.substr( 0, first )).c_str(), &endptr, 10 ); if( *endptr != '\0' ) { L << Logger::Warning << m_myname << " Invalid " << attrname << " without priority for " << m_qname << ": " << content << endl; diff -Nur pdns-2.9.21.1.orig/modules/ldapbackend/ldapbackend.cc.orig pdns-2.9.21.1.int16/modules/ldapbackend/ldapbackend.cc.orig --- pdns-2.9.21.1.orig/modules/ldapbackend/ldapbackend.cc.orig 1969-12-31 17:00:00.000000000 -0700 +++ pdns-2.9.21.1.int16/modules/ldapbackend/ldapbackend.cc.orig 2007-04-21 07:56:36.000000000 -0600 @@ -0,0 +1,566 @@ +#include "ldapbackend.hh" + + + +unsigned int ldap_host_index = 0; + + + +LdapBackend::LdapBackend( const string &suffix ) +{ + string hoststr; + unsigned int i, idx; + vector hosts; + + + try + { + m_msgid = 0; + m_qname = ""; + m_pldap = NULL; + m_qlog = arg().mustDo( "query-logging" ); + m_default_ttl = arg().asNum( "default-ttl" ); + m_myname = "[LdapBackend]"; + + // we need UTC time for timestamps + setenv( "TZ", "", 1 ); tzset(); + + setArgPrefix( "ldap" + suffix ); + + m_getdn = false; + m_list_fcnt = &LdapBackend::list_simple; + m_lookup_fcnt = &LdapBackend::lookup_simple; + m_prepare_fcnt = &LdapBackend::prepare_simple; + + if( getArg( "method" ) == "tree" ) + { + m_lookup_fcnt = &LdapBackend::lookup_tree; + } + + if( getArg( "method" ) == "strict" || mustDo( "disable-ptrrecord" ) ) + { + m_list_fcnt = &LdapBackend::list_strict; + m_lookup_fcnt = &LdapBackend::lookup_strict; + m_prepare_fcnt = &LdapBackend::prepare_strict; + } + + stringtok( hosts, getArg( "host" ), ", " ); + idx = ldap_host_index++ % hosts.size(); + hoststr = hosts[idx]; + + for( i = 1; i < hosts.size(); i++ ) + { + hoststr += " " + hosts[ ( idx + i ) % hosts.size() ]; + } + + L << Logger::Info << m_myname << " LDAP servers = " << hoststr << endl; + + m_pldap = new PowerLDAP( hoststr.c_str(), LDAP_PORT, mustDo( "starttls" ) ); + m_pldap->setOption( LDAP_OPT_DEREF, LDAP_DEREF_ALWAYS ); + m_pldap->simpleBind( getArg( "binddn" ), getArg( "secret" ) ); + } + catch( LDAPException &le ) + { + if( m_pldap != NULL ) { delete( m_pldap ); } + L << Logger::Error << m_myname << " Ldap connection to server failed: " << le.what() << endl; + throw( AhuException( "Unable to connect to ldap server" ) ); + } + catch( exception &e ) + { + if( m_pldap != NULL ) { delete( m_pldap ); } + L << Logger::Error << m_myname << " Caught STL exception: " << e.what() << endl; + throw( AhuException( "Unable to connect to ldap server" ) ); + } + + L << Logger::Notice << m_myname << " Ldap connection succeeded" << endl; +} + + + +LdapBackend::~LdapBackend() +{ + if( m_pldap != NULL ) { delete( m_pldap ); } + L << Logger::Notice << m_myname << " Ldap connection closed" << endl; +} + + + +bool LdapBackend::list( const string& target, int domain_id ) +{ + try + { + m_qname = target; + m_axfrqlen = target.length(); + m_adomain = m_adomains.end(); // skip loops in get() first time + + return (this->*m_list_fcnt)( target, domain_id ); + } + catch( LDAPTimeout < ) + { + L << Logger::Warning << m_myname << " Unable to get zone " + target + " from LDAP directory: " << lt.what() << endl; + throw( DBException( "LDAP server timeout" ) ); + } + catch( LDAPException &le ) + { + L << Logger::Error << m_myname << " Unable to get zone " + target + " from LDAP directory: " << le.what() << endl; + throw( AhuException( "LDAP server unreachable" ) ); // try to reconnect to another server + } + catch( exception &e ) + { + L << Logger::Error << m_myname << " Caught STL exception for target " << target << ": " << e.what() << endl; + throw( DBException( "STL exception" ) ); + } + + return false; +} + + + +inline bool LdapBackend::list_simple( const string& target, int domain_id ) +{ + string dn; + string filter; + string qesc; + + + dn = getArg( "basedn" ); + qesc = toLower( m_pldap->escape( target ) ); + + // search for SOARecord of target + filter = strbind( ":target:", "associatedDomain=" + qesc, getArg( "filter-axfr" ) ); + m_msgid = m_pldap->search( dn, LDAP_SCOPE_SUBTREE, filter, (const char**) ldap_attrany ); + m_pldap->getSearchEntry( m_msgid, m_result, true ); + + if( m_result.count( "dn" ) && !m_result["dn"].empty() ) + { + dn = m_result["dn"][0]; + m_result.erase( "dn" ); + } + + prepare(); + filter = strbind( ":target:", "associatedDomain=*." + qesc, getArg( "filter-axfr" ) ); + DLOG( L << Logger::Debug << m_myname << " Search = basedn: " << dn << ", filter: " << filter << endl ); + m_msgid = m_pldap->search( dn, LDAP_SCOPE_SUBTREE, filter, (const char**) ldap_attrany ); + + return true; +} + + + +inline bool LdapBackend::list_strict( const string& target, int domain_id ) +{ + if( target.size() > 13 && target.substr( target.size() - 13, 13 ) == ".in-addr.arpa" || + target.size() > 9 && target.substr( target.size() - 9, 9 ) == ".ip6.arpa" ) + { + L << Logger::Warning << m_myname << " Request for reverse zone AXFR, but this is not supported in strict mode" << endl; + return false; // AXFR isn't supported in strict mode. Use simple mode and additional PTR records + } + + return list_simple( target, domain_id ); +} + + + +void LdapBackend::lookup( const QType &qtype, const string &qname, DNSPacket *dnspkt, int zoneid ) +{ + try + { + m_axfrqlen = 0; + m_qname = qname; + m_adomain = m_adomains.end(); // skip loops in get() first time + + if( m_qlog ) { L.log( "Query: '" + qname + "|" + qtype.getName() + "'", Logger::Error ); } + (this->*m_lookup_fcnt)( qtype, qname, dnspkt, zoneid ); + } + catch( LDAPTimeout < ) + { + L << Logger::Warning << m_myname << " Unable to search LDAP directory: " << lt.what() << endl; + throw( DBException( "LDAP server timeout" ) ); + } + catch( LDAPException &le ) + { + L << Logger::Error << m_myname << " Unable to search LDAP directory: " << le.what() << endl; + throw( AhuException( "LDAP server unreachable" ) ); // try to reconnect to another server + } + catch( exception &e ) + { + L << Logger::Error << m_myname << " Caught STL exception for qname " << qname << ": " << e.what() << endl; + throw( DBException( "STL exception" ) ); + } +} + + + +void LdapBackend::lookup_simple( const QType &qtype, const string &qname, DNSPacket *dnspkt, int zoneid ) +{ + string filter, attr, qesc; + char** attributes = ldap_attrany + 1; // skip associatedDomain + char* attronly[] = { NULL, "dNSTTL", "modifyTimestamp", NULL }; + + + qesc = toLower( m_pldap->escape( qname ) ); + filter = "associatedDomain=" + qesc; + + if( qtype.getCode() != QType::ANY ) + { + attr = qtype.getName() + "Record"; + filter = "&(" + filter + ")(" + attr + "=*)"; + attronly[0] = (char*) attr.c_str(); + attributes = attronly; + } + + filter = strbind( ":target:", filter, getArg( "filter-lookup" ) ); + + DLOG( L << Logger::Debug << m_myname << " Search = basedn: " << getArg( "basedn" ) << ", filter: " << filter << ", qtype: " << qtype.getName() << endl ); + m_msgid = m_pldap->search( getArg( "basedn" ), LDAP_SCOPE_SUBTREE, filter, (const char**) attributes ); +} + + + +void LdapBackend::lookup_strict( const QType &qtype, const string &qname, DNSPacket *dnspkt, int zoneid ) +{ + int len; + vector parts; + string filter, attr, qesc; + char** attributes = ldap_attrany + 1; // skip associatedDomain + char* attronly[] = { NULL, "dNSTTL", "modifyTimestamp", NULL }; + + + qesc = toLower( m_pldap->escape( qname ) ); + stringtok( parts, qesc, "." ); + len = qesc.length(); + + if( parts.size() == 6 && len > 13 && qesc.substr( len - 13, 13 ) == ".in-addr.arpa" ) // IPv4 reverse lookups + { + filter = "aRecord=" + ptr2ip4( parts ); + attronly[0] = "associatedDomain"; + attributes = attronly; + } + else if( parts.size() == 34 && len > 9 && ( qesc.substr( len - 9, 9 ) == ".ip6.arpa" ) ) // IPv6 reverse lookups + { + filter = "aAAARecord=" + ptr2ip6( parts ); + attronly[0] = "associatedDomain"; + attributes = attronly; + } + else // IPv4 and IPv6 lookups + { + filter = "associatedDomain=" + qesc; + if( qtype.getCode() != QType::ANY ) + { + attr = qtype.getName() + "Record"; + filter = "&(" + filter + ")(" + attr + "=*)"; + attronly[0] = (char*) attr.c_str(); + attributes = attronly; + } + } + + filter = strbind( ":target:", filter, getArg( "filter-lookup" ) ); + + DLOG( L << Logger::Debug << m_myname << " Search = basedn: " << getArg( "basedn" ) << ", filter: " << filter << ", qtype: " << qtype.getName() << endl ); + m_msgid = m_pldap->search( getArg( "basedn" ), LDAP_SCOPE_SUBTREE, filter, (const char**) attributes ); +} + + + +void LdapBackend::lookup_tree( const QType &qtype, const string &qname, DNSPacket *dnspkt, int zoneid ) +{ + string filter, attr, qesc, dn; + char** attributes = ldap_attrany + 1; // skip associatedDomain + char* attronly[] = { NULL, "dNSTTL", "modifyTimestamp", NULL }; + vector::reverse_iterator i; + vector parts; + + + qesc = toLower( m_pldap->escape( qname ) ); + filter = "associatedDomain=" + qesc; + + if( qtype.getCode() != QType::ANY ) + { + attr = qtype.getName() + "Record"; + filter = "&(" + filter + ")(" + attr + "=*)"; + attronly[0] = (char*) attr.c_str(); + attributes = attronly; + } + + filter = strbind( ":target:", filter, getArg( "filter-lookup" ) ); + + stringtok( parts, toLower( qname ), "." ); + for( i = parts.rbegin(); i != parts.rend(); i++ ) + { + dn = "dc=" + *i + "," + dn; + } + + DLOG( L << Logger::Debug << m_myname << " Search = basedn: " << dn + getArg( "basedn" ) << ", filter: " << filter << ", qtype: " << qtype.getName() << endl ); + m_msgid = m_pldap->search( dn + getArg( "basedn" ), LDAP_SCOPE_BASE, filter, (const char**) attributes ); +} + + +inline bool LdapBackend::prepare() +{ + m_adomains.clear(); + m_ttl = m_default_ttl; + m_last_modified = 0; + + if( m_result.count( "dNSTTL" ) && !m_result["dNSTTL"].empty() ) + { + char* endptr; + + m_ttl = (uint32_t) strtol( m_result["dNSTTL"][0].c_str(), &endptr, 10 ); + if( *endptr != '\0' ) + { + L << Logger::Warning << m_myname << " Invalid time to life for " << m_qname << ": " << m_result["dNSTTL"][0] << endl; + m_ttl = m_default_ttl; + } + m_result.erase( "dNSTTL" ); + } + + if( m_result.count( "modifyTimestamp" ) && !m_result["modifyTimestamp"].empty() ) + { + if( ( m_last_modified = str2tstamp( m_result["modifyTimestamp"][0] ) ) == 0 ) + { + L << Logger::Warning << m_myname << " Invalid modifyTimestamp for " << m_qname << ": " << m_result["modifyTimestamp"][0] << endl; + } + m_result.erase( "modifyTimestamp" ); + } + + if( !(this->*m_prepare_fcnt)() ) + { + return false; + } + + m_adomain = m_adomains.begin(); + m_attribute = m_result.begin(); + m_value = m_attribute->second.begin(); + + return true; +} + + + +inline bool LdapBackend::prepare_simple() +{ + if( !m_axfrqlen ) // request was a normal lookup() + { + m_adomains.push_back( m_qname ); + } + else // request was a list() for AXFR + { + if( m_result.count( "associatedDomain" ) ) + { + vector::iterator i; + for( i = m_result["associatedDomain"].begin(); i != m_result["associatedDomain"].end(); i++ ) { + if( i->size() >= m_axfrqlen && i->substr( i->size() - m_axfrqlen, m_axfrqlen ) == m_qname ) { + m_adomains.push_back( *i ); + } + } + m_result.erase( "associatedDomain" ); + } + } + + return true; +} + + + +inline bool LdapBackend::prepare_strict() +{ + if( !m_axfrqlen ) // request was a normal lookup() + { + m_adomains.push_back( m_qname ); + if( m_result.count( "associatedDomain" ) ) + { + m_result["PTRRecord"] = m_result["associatedDomain"]; + m_result.erase( "associatedDomain" ); + } + } + else // request was a list() for AXFR + { + if( m_result.count( "associatedDomain" ) ) + { + vector::iterator i; + for( i = m_result["associatedDomain"].begin(); i != m_result["associatedDomain"].end(); i++ ) { + if( i->size() >= m_axfrqlen && i->substr( i->size() - m_axfrqlen, m_axfrqlen ) == m_qname ) { + m_adomains.push_back( *i ); + } + } + m_result.erase( "associatedDomain" ); + } + } + + return true; +} + + + +bool LdapBackend::get( DNSResourceRecord &rr ) +{ + QType qt; + vector parts; + string attrname, content, qstr; + + + try + { + do + { + while( m_adomain != m_adomains.end() ) + { + while( m_attribute != m_result.end() ) + { + attrname = m_attribute->first; + qstr = attrname.substr( 0, attrname.length() - 6 ); // extract qtype string from ldap attribute name + qt = QType( const_cast(toUpper( qstr ).c_str()) ); + + while( m_value != m_attribute->second.end() ) + { + content = *m_value; + + rr.qtype = qt; + rr.qname = *m_adomain; + rr.priority = 0; + rr.ttl = m_ttl; + rr.last_modified = m_last_modified; + + if( qt.getCode() == QType::MX || qt.getCode() == QType::SRV ) // Priority, e.g. 10 smtp.example.com + { + char* endptr; + string::size_type first = content.find_first_of( " " ); + + if( first == string::npos ) + { + L << Logger::Warning << m_myname << " Invalid " << attrname << " without priority for " << m_qname << ": " << content << endl; + m_value++; + continue; + } + + rr.priority = (uint16_t) strtoul( (content.substr( 0, first )).c_str(), &endptr, 10 ); + if( *endptr != '\0' ) + { + L << Logger::Warning << m_myname << " Invalid " << attrname << " without priority for " << m_qname << ": " << content << endl; + m_value++; + continue; + } + + content = content.substr( first + 1, content.length() - first - 1 ); + } + + rr.content = content; + m_value++; + + DLOG( L << Logger::Debug << m_myname << " Record = qname: " << rr.qname << ", qtype: " << (rr.qtype).getName() << ", priority: " << rr.priority << ", ttl: " << rr.ttl << ", content: " << rr.content << endl ); + return true; + } + + m_attribute++; + m_value = m_attribute->second.begin(); + } + m_adomain++; + m_attribute = m_result.begin(); + m_value = m_attribute->second.begin(); + } + } + while( m_pldap->getSearchEntry( m_msgid, m_result, m_getdn ) && prepare() ); + + } + catch( LDAPTimeout < ) + { + L << Logger::Warning << m_myname << " Search failed: " << lt.what() << endl; + throw( DBException( "LDAP server timeout" ) ); + } + catch( LDAPException &le ) + { + L << Logger::Error << m_myname << " Search failed: " << le.what() << endl; + throw( AhuException( "LDAP server unreachable" ) ); // try to reconnect to another server + } + catch( exception &e ) + { + L << Logger::Error << m_myname << " Caught STL exception for " << m_qname << ": " << e.what() << endl; + throw( DBException( "STL exception" ) ); + } + + return false; +} + + + + bool LdapBackend::getDomainInfo( const string& domain, DomainInfo& di ) +{ + string filter; + SOAData sd; + char* attronly[] = { "sOARecord", NULL }; + + + // search for SOARecord of domain + filter = "(&(associatedDomain=" + toLower( m_pldap->escape( domain ) ) + ")(SOARecord=*))"; + m_msgid = m_pldap->search( getArg( "basedn" ), LDAP_SCOPE_SUBTREE, filter, (const char**) attronly ); + m_pldap->getSearchEntry( m_msgid, m_result ); + + if( m_result.count( "sOARecord" ) && !m_result["sOARecord"].empty() ) + { + sd.serial = 0; + fillSOAData( m_result["sOARecord"][0], sd ); + + di.id = 0; + di.serial = sd.serial; + di.zone = domain; + di.last_check = 0; + di.backend = this; + di.kind = DomainInfo::Master; + + return true; + } + + return false; +} + + + + + +class LdapFactory : public BackendFactory +{ + +public: + + LdapFactory() : BackendFactory( "ldap" ) {} + + + void declareArguments( const string &suffix="" ) + { + declare( suffix, "host", "One or more LDAP server with ports or LDAP URIs (separated by spaces)","ldap://127.0.0.1:389/" ); + declare( suffix, "starttls", "Use TLS to encrypt connection (unused for LDAP URIs)", "no" ); + declare( suffix, "basedn", "Search root in ldap tree (must be set)","" ); + declare( suffix, "binddn", "User dn for non anonymous binds","" ); + declare( suffix, "secret", "User password for non anonymous binds", "" ); + declare( suffix, "method", "How to search entries (simple, strict or tree)", "simple" ); + declare( suffix, "filter-axfr", "LDAP filter for limiting AXFR results", "(:target:)" ); + declare( suffix, "filter-lookup", "LDAP filter for limiting IP or name lookups", "(:target:)" ); + declare( suffix, "disable-ptrrecord", "Deprecated, use ldap-method=strict instead", "no" ); + } + + + DNSBackend* make( const string &suffix="" ) + { + return new LdapBackend( suffix ); + } +}; + + + + + +class LdapLoader +{ + LdapFactory factory; + +public: + + LdapLoader() + { + BackendMakers().report( &factory ); + L << Logger::Info << " [LdapBackend] This is the ldap module version "VERSION" ("__DATE__", "__TIME__") reporting" << endl; + } +}; + + +static LdapLoader ldaploader; diff -Nur pdns-2.9.21.1.orig/modules/ldapbackend/powerldap.cc pdns-2.9.21.1.int16/modules/ldapbackend/powerldap.cc --- pdns-2.9.21.1.orig/modules/ldapbackend/powerldap.cc 2007-04-21 07:56:36.000000000 -0600 +++ pdns-2.9.21.1.int16/modules/ldapbackend/powerldap.cc 2008-10-06 10:08:55.000000000 -0600 @@ -2,7 +2,7 @@ -PowerLDAP::PowerLDAP( const string& hosts, uint16_t port, bool tls ) +PowerLDAP::PowerLDAP( const string& hosts, u_int16_t port, bool tls ) { int protocol = LDAP_VERSION3; diff -Nur pdns-2.9.21.1.orig/modules/ldapbackend/powerldap.hh pdns-2.9.21.1.int16/modules/ldapbackend/powerldap.hh --- pdns-2.9.21.1.orig/modules/ldapbackend/powerldap.hh 2007-04-21 07:56:36.000000000 -0600 +++ pdns-2.9.21.1.int16/modules/ldapbackend/powerldap.hh 2008-10-06 10:11:53.000000000 -0600 @@ -32,12 +32,8 @@ #include #endif -#ifdef HAVE_STDINT_H #include -#else #include -#endif - #ifndef POWERLDAP_HH @@ -78,7 +74,7 @@ typedef map > sentry_t; typedef vector sresult_t; - PowerLDAP( const string& hosts = "ldap://127.0.0.1/", uint16_t port = LDAP_PORT, bool tls = false ); + PowerLDAP( const string& hosts = "ldap://127.0.0.1/", u_int16_t port = LDAP_PORT, bool tls = false ); ~PowerLDAP(); void getOption( int option, int* value ); diff -Nur pdns-2.9.21.1.orig/modules/ldapbackend/powerldap.hh.orig pdns-2.9.21.1.int16/modules/ldapbackend/powerldap.hh.orig --- pdns-2.9.21.1.orig/modules/ldapbackend/powerldap.hh.orig 1969-12-31 17:00:00.000000000 -0700 +++ pdns-2.9.21.1.int16/modules/ldapbackend/powerldap.hh.orig 2007-04-21 07:56:36.000000000 -0600 @@ -0,0 +1,98 @@ +/* + * PowerDNS LDAP Connector + * By PowerDNS.COM BV + * By Norbert Sendetzky (2003-2007) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_STDINT_H +#include +#else +#include +#endif + + + +#ifndef POWERLDAP_HH +#define POWERLDAP_HH + + + +using std::map; +using std::string; +using std::vector; + + + +class LDAPException : public std::runtime_error +{ +public: + explicit LDAPException( const string &str ) : std::runtime_error( str ) {} +}; + + + +class LDAPTimeout : public LDAPException +{ +public: + explicit LDAPTimeout() : LDAPException( "Timeout" ) {} +}; + + + +class PowerLDAP +{ + LDAP* d_ld; + + const string getError( int rc = -1 ); + int waitResult( int msgid = LDAP_RES_ANY, int timeout = 0, LDAPMessage** result = NULL ); + +public: + typedef map > sentry_t; + typedef vector sresult_t; + + PowerLDAP( const string& hosts = "ldap://127.0.0.1/", uint16_t port = LDAP_PORT, bool tls = false ); + ~PowerLDAP(); + + void getOption( int option, int* value ); + void setOption( int option, int value ); + + void simpleBind( const string& ldapbinddn = "", const string& ldapsecret = "" ); + int search( const string& base, int scope, const string& filter, const char** attr = 0 ); + + bool getSearchEntry( int msgid, sentry_t& entry, bool dn = false, int timeout = 5 ); + void getSearchResults( int msgid, sresult_t& result, bool dn = false, int timeout = 5 ); + + static const string escape( const string& tobe ); +}; + + + +#endif