1 diff -Nur pdns-2.9.21.1.orig/modules/ldapbackend/ldapbackend.cc pdns-2.9.21.1.int16/modules/ldapbackend/ldapbackend.cc
2 --- pdns-2.9.21.1.orig/modules/ldapbackend/ldapbackend.cc 2007-04-21 07:56:36.000000000 -0600
3 +++ pdns-2.9.21.1.int16/modules/ldapbackend/ldapbackend.cc 2008-10-06 10:08:55.000000000 -0600
8 - rr.priority = (uint16_t) strtoul( (content.substr( 0, first )).c_str(), &endptr, 10 );
9 + rr.priority = (u_int16_t) strtoul( (content.substr( 0, first )).c_str(), &endptr, 10 );
12 L << Logger::Warning << m_myname << " Invalid " << attrname << " without priority for " << m_qname << ": " << content << endl;
13 diff -Nur pdns-2.9.21.1.orig/modules/ldapbackend/ldapbackend.cc.orig pdns-2.9.21.1.int16/modules/ldapbackend/ldapbackend.cc.orig
14 --- pdns-2.9.21.1.orig/modules/ldapbackend/ldapbackend.cc.orig 1969-12-31 17:00:00.000000000 -0700
15 +++ pdns-2.9.21.1.int16/modules/ldapbackend/ldapbackend.cc.orig 2007-04-21 07:56:36.000000000 -0600
17 +#include "ldapbackend.hh"
21 +unsigned int ldap_host_index = 0;
25 +LdapBackend::LdapBackend( const string &suffix )
28 + unsigned int i, idx;
29 + vector<string> hosts;
37 + m_qlog = arg().mustDo( "query-logging" );
38 + m_default_ttl = arg().asNum( "default-ttl" );
39 + m_myname = "[LdapBackend]";
41 + // we need UTC time for timestamps
42 + setenv( "TZ", "", 1 ); tzset();
44 + setArgPrefix( "ldap" + suffix );
47 + m_list_fcnt = &LdapBackend::list_simple;
48 + m_lookup_fcnt = &LdapBackend::lookup_simple;
49 + m_prepare_fcnt = &LdapBackend::prepare_simple;
51 + if( getArg( "method" ) == "tree" )
53 + m_lookup_fcnt = &LdapBackend::lookup_tree;
56 + if( getArg( "method" ) == "strict" || mustDo( "disable-ptrrecord" ) )
58 + m_list_fcnt = &LdapBackend::list_strict;
59 + m_lookup_fcnt = &LdapBackend::lookup_strict;
60 + m_prepare_fcnt = &LdapBackend::prepare_strict;
63 + stringtok( hosts, getArg( "host" ), ", " );
64 + idx = ldap_host_index++ % hosts.size();
65 + hoststr = hosts[idx];
67 + for( i = 1; i < hosts.size(); i++ )
69 + hoststr += " " + hosts[ ( idx + i ) % hosts.size() ];
72 + L << Logger::Info << m_myname << " LDAP servers = " << hoststr << endl;
74 + m_pldap = new PowerLDAP( hoststr.c_str(), LDAP_PORT, mustDo( "starttls" ) );
75 + m_pldap->setOption( LDAP_OPT_DEREF, LDAP_DEREF_ALWAYS );
76 + m_pldap->simpleBind( getArg( "binddn" ), getArg( "secret" ) );
78 + catch( LDAPException &le )
80 + if( m_pldap != NULL ) { delete( m_pldap ); }
81 + L << Logger::Error << m_myname << " Ldap connection to server failed: " << le.what() << endl;
82 + throw( AhuException( "Unable to connect to ldap server" ) );
84 + catch( exception &e )
86 + if( m_pldap != NULL ) { delete( m_pldap ); }
87 + L << Logger::Error << m_myname << " Caught STL exception: " << e.what() << endl;
88 + throw( AhuException( "Unable to connect to ldap server" ) );
91 + L << Logger::Notice << m_myname << " Ldap connection succeeded" << endl;
96 +LdapBackend::~LdapBackend()
98 + if( m_pldap != NULL ) { delete( m_pldap ); }
99 + L << Logger::Notice << m_myname << " Ldap connection closed" << endl;
104 +bool LdapBackend::list( const string& target, int domain_id )
109 + m_axfrqlen = target.length();
110 + m_adomain = m_adomains.end(); // skip loops in get() first time
112 + return (this->*m_list_fcnt)( target, domain_id );
114 + catch( LDAPTimeout < )
116 + L << Logger::Warning << m_myname << " Unable to get zone " + target + " from LDAP directory: " << lt.what() << endl;
117 + throw( DBException( "LDAP server timeout" ) );
119 + catch( LDAPException &le )
121 + L << Logger::Error << m_myname << " Unable to get zone " + target + " from LDAP directory: " << le.what() << endl;
122 + throw( AhuException( "LDAP server unreachable" ) ); // try to reconnect to another server
124 + catch( exception &e )
126 + L << Logger::Error << m_myname << " Caught STL exception for target " << target << ": " << e.what() << endl;
127 + throw( DBException( "STL exception" ) );
135 +inline bool LdapBackend::list_simple( const string& target, int domain_id )
142 + dn = getArg( "basedn" );
143 + qesc = toLower( m_pldap->escape( target ) );
145 + // search for SOARecord of target
146 + filter = strbind( ":target:", "associatedDomain=" + qesc, getArg( "filter-axfr" ) );
147 + m_msgid = m_pldap->search( dn, LDAP_SCOPE_SUBTREE, filter, (const char**) ldap_attrany );
148 + m_pldap->getSearchEntry( m_msgid, m_result, true );
150 + if( m_result.count( "dn" ) && !m_result["dn"].empty() )
152 + dn = m_result["dn"][0];
153 + m_result.erase( "dn" );
157 + filter = strbind( ":target:", "associatedDomain=*." + qesc, getArg( "filter-axfr" ) );
158 + DLOG( L << Logger::Debug << m_myname << " Search = basedn: " << dn << ", filter: " << filter << endl );
159 + m_msgid = m_pldap->search( dn, LDAP_SCOPE_SUBTREE, filter, (const char**) ldap_attrany );
166 +inline bool LdapBackend::list_strict( const string& target, int domain_id )
168 + if( target.size() > 13 && target.substr( target.size() - 13, 13 ) == ".in-addr.arpa" ||
169 + target.size() > 9 && target.substr( target.size() - 9, 9 ) == ".ip6.arpa" )
171 + L << Logger::Warning << m_myname << " Request for reverse zone AXFR, but this is not supported in strict mode" << endl;
172 + return false; // AXFR isn't supported in strict mode. Use simple mode and additional PTR records
175 + return list_simple( target, domain_id );
180 +void LdapBackend::lookup( const QType &qtype, const string &qname, DNSPacket *dnspkt, int zoneid )
186 + m_adomain = m_adomains.end(); // skip loops in get() first time
188 + if( m_qlog ) { L.log( "Query: '" + qname + "|" + qtype.getName() + "'", Logger::Error ); }
189 + (this->*m_lookup_fcnt)( qtype, qname, dnspkt, zoneid );
191 + catch( LDAPTimeout < )
193 + L << Logger::Warning << m_myname << " Unable to search LDAP directory: " << lt.what() << endl;
194 + throw( DBException( "LDAP server timeout" ) );
196 + catch( LDAPException &le )
198 + L << Logger::Error << m_myname << " Unable to search LDAP directory: " << le.what() << endl;
199 + throw( AhuException( "LDAP server unreachable" ) ); // try to reconnect to another server
201 + catch( exception &e )
203 + L << Logger::Error << m_myname << " Caught STL exception for qname " << qname << ": " << e.what() << endl;
204 + throw( DBException( "STL exception" ) );
210 +void LdapBackend::lookup_simple( const QType &qtype, const string &qname, DNSPacket *dnspkt, int zoneid )
212 + string filter, attr, qesc;
213 + char** attributes = ldap_attrany + 1; // skip associatedDomain
214 + char* attronly[] = { NULL, "dNSTTL", "modifyTimestamp", NULL };
217 + qesc = toLower( m_pldap->escape( qname ) );
218 + filter = "associatedDomain=" + qesc;
220 + if( qtype.getCode() != QType::ANY )
222 + attr = qtype.getName() + "Record";
223 + filter = "&(" + filter + ")(" + attr + "=*)";
224 + attronly[0] = (char*) attr.c_str();
225 + attributes = attronly;
228 + filter = strbind( ":target:", filter, getArg( "filter-lookup" ) );
230 + DLOG( L << Logger::Debug << m_myname << " Search = basedn: " << getArg( "basedn" ) << ", filter: " << filter << ", qtype: " << qtype.getName() << endl );
231 + m_msgid = m_pldap->search( getArg( "basedn" ), LDAP_SCOPE_SUBTREE, filter, (const char**) attributes );
236 +void LdapBackend::lookup_strict( const QType &qtype, const string &qname, DNSPacket *dnspkt, int zoneid )
239 + vector<string> parts;
240 + string filter, attr, qesc;
241 + char** attributes = ldap_attrany + 1; // skip associatedDomain
242 + char* attronly[] = { NULL, "dNSTTL", "modifyTimestamp", NULL };
245 + qesc = toLower( m_pldap->escape( qname ) );
246 + stringtok( parts, qesc, "." );
247 + len = qesc.length();
249 + if( parts.size() == 6 && len > 13 && qesc.substr( len - 13, 13 ) == ".in-addr.arpa" ) // IPv4 reverse lookups
251 + filter = "aRecord=" + ptr2ip4( parts );
252 + attronly[0] = "associatedDomain";
253 + attributes = attronly;
255 + else if( parts.size() == 34 && len > 9 && ( qesc.substr( len - 9, 9 ) == ".ip6.arpa" ) ) // IPv6 reverse lookups
257 + filter = "aAAARecord=" + ptr2ip6( parts );
258 + attronly[0] = "associatedDomain";
259 + attributes = attronly;
261 + else // IPv4 and IPv6 lookups
263 + filter = "associatedDomain=" + qesc;
264 + if( qtype.getCode() != QType::ANY )
266 + attr = qtype.getName() + "Record";
267 + filter = "&(" + filter + ")(" + attr + "=*)";
268 + attronly[0] = (char*) attr.c_str();
269 + attributes = attronly;
273 + filter = strbind( ":target:", filter, getArg( "filter-lookup" ) );
275 + DLOG( L << Logger::Debug << m_myname << " Search = basedn: " << getArg( "basedn" ) << ", filter: " << filter << ", qtype: " << qtype.getName() << endl );
276 + m_msgid = m_pldap->search( getArg( "basedn" ), LDAP_SCOPE_SUBTREE, filter, (const char**) attributes );
281 +void LdapBackend::lookup_tree( const QType &qtype, const string &qname, DNSPacket *dnspkt, int zoneid )
283 + string filter, attr, qesc, dn;
284 + char** attributes = ldap_attrany + 1; // skip associatedDomain
285 + char* attronly[] = { NULL, "dNSTTL", "modifyTimestamp", NULL };
286 + vector<string>::reverse_iterator i;
287 + vector<string> parts;
290 + qesc = toLower( m_pldap->escape( qname ) );
291 + filter = "associatedDomain=" + qesc;
293 + if( qtype.getCode() != QType::ANY )
295 + attr = qtype.getName() + "Record";
296 + filter = "&(" + filter + ")(" + attr + "=*)";
297 + attronly[0] = (char*) attr.c_str();
298 + attributes = attronly;
301 + filter = strbind( ":target:", filter, getArg( "filter-lookup" ) );
303 + stringtok( parts, toLower( qname ), "." );
304 + for( i = parts.rbegin(); i != parts.rend(); i++ )
306 + dn = "dc=" + *i + "," + dn;
309 + DLOG( L << Logger::Debug << m_myname << " Search = basedn: " << dn + getArg( "basedn" ) << ", filter: " << filter << ", qtype: " << qtype.getName() << endl );
310 + m_msgid = m_pldap->search( dn + getArg( "basedn" ), LDAP_SCOPE_BASE, filter, (const char**) attributes );
314 +inline bool LdapBackend::prepare()
316 + m_adomains.clear();
317 + m_ttl = m_default_ttl;
318 + m_last_modified = 0;
320 + if( m_result.count( "dNSTTL" ) && !m_result["dNSTTL"].empty() )
324 + m_ttl = (uint32_t) strtol( m_result["dNSTTL"][0].c_str(), &endptr, 10 );
325 + if( *endptr != '\0' )
327 + L << Logger::Warning << m_myname << " Invalid time to life for " << m_qname << ": " << m_result["dNSTTL"][0] << endl;
328 + m_ttl = m_default_ttl;
330 + m_result.erase( "dNSTTL" );
333 + if( m_result.count( "modifyTimestamp" ) && !m_result["modifyTimestamp"].empty() )
335 + if( ( m_last_modified = str2tstamp( m_result["modifyTimestamp"][0] ) ) == 0 )
337 + L << Logger::Warning << m_myname << " Invalid modifyTimestamp for " << m_qname << ": " << m_result["modifyTimestamp"][0] << endl;
339 + m_result.erase( "modifyTimestamp" );
342 + if( !(this->*m_prepare_fcnt)() )
347 + m_adomain = m_adomains.begin();
348 + m_attribute = m_result.begin();
349 + m_value = m_attribute->second.begin();
356 +inline bool LdapBackend::prepare_simple()
358 + if( !m_axfrqlen ) // request was a normal lookup()
360 + m_adomains.push_back( m_qname );
362 + else // request was a list() for AXFR
364 + if( m_result.count( "associatedDomain" ) )
366 + vector<string>::iterator i;
367 + for( i = m_result["associatedDomain"].begin(); i != m_result["associatedDomain"].end(); i++ ) {
368 + if( i->size() >= m_axfrqlen && i->substr( i->size() - m_axfrqlen, m_axfrqlen ) == m_qname ) {
369 + m_adomains.push_back( *i );
372 + m_result.erase( "associatedDomain" );
381 +inline bool LdapBackend::prepare_strict()
383 + if( !m_axfrqlen ) // request was a normal lookup()
385 + m_adomains.push_back( m_qname );
386 + if( m_result.count( "associatedDomain" ) )
388 + m_result["PTRRecord"] = m_result["associatedDomain"];
389 + m_result.erase( "associatedDomain" );
392 + else // request was a list() for AXFR
394 + if( m_result.count( "associatedDomain" ) )
396 + vector<string>::iterator i;
397 + for( i = m_result["associatedDomain"].begin(); i != m_result["associatedDomain"].end(); i++ ) {
398 + if( i->size() >= m_axfrqlen && i->substr( i->size() - m_axfrqlen, m_axfrqlen ) == m_qname ) {
399 + m_adomains.push_back( *i );
402 + m_result.erase( "associatedDomain" );
411 +bool LdapBackend::get( DNSResourceRecord &rr )
414 + vector<string> parts;
415 + string attrname, content, qstr;
422 + while( m_adomain != m_adomains.end() )
424 + while( m_attribute != m_result.end() )
426 + attrname = m_attribute->first;
427 + qstr = attrname.substr( 0, attrname.length() - 6 ); // extract qtype string from ldap attribute name
428 + qt = QType( const_cast<char*>(toUpper( qstr ).c_str()) );
430 + while( m_value != m_attribute->second.end() )
432 + content = *m_value;
435 + rr.qname = *m_adomain;
438 + rr.last_modified = m_last_modified;
440 + if( qt.getCode() == QType::MX || qt.getCode() == QType::SRV ) // Priority, e.g. 10 smtp.example.com
443 + string::size_type first = content.find_first_of( " " );
445 + if( first == string::npos )
447 + L << Logger::Warning << m_myname << " Invalid " << attrname << " without priority for " << m_qname << ": " << content << endl;
452 + rr.priority = (uint16_t) strtoul( (content.substr( 0, first )).c_str(), &endptr, 10 );
453 + if( *endptr != '\0' )
455 + L << Logger::Warning << m_myname << " Invalid " << attrname << " without priority for " << m_qname << ": " << content << endl;
460 + content = content.substr( first + 1, content.length() - first - 1 );
463 + rr.content = content;
466 + DLOG( L << Logger::Debug << m_myname << " Record = qname: " << rr.qname << ", qtype: " << (rr.qtype).getName() << ", priority: " << rr.priority << ", ttl: " << rr.ttl << ", content: " << rr.content << endl );
471 + m_value = m_attribute->second.begin();
474 + m_attribute = m_result.begin();
475 + m_value = m_attribute->second.begin();
478 + while( m_pldap->getSearchEntry( m_msgid, m_result, m_getdn ) && prepare() );
481 + catch( LDAPTimeout < )
483 + L << Logger::Warning << m_myname << " Search failed: " << lt.what() << endl;
484 + throw( DBException( "LDAP server timeout" ) );
486 + catch( LDAPException &le )
488 + L << Logger::Error << m_myname << " Search failed: " << le.what() << endl;
489 + throw( AhuException( "LDAP server unreachable" ) ); // try to reconnect to another server
491 + catch( exception &e )
493 + L << Logger::Error << m_myname << " Caught STL exception for " << m_qname << ": " << e.what() << endl;
494 + throw( DBException( "STL exception" ) );
502 + bool LdapBackend::getDomainInfo( const string& domain, DomainInfo& di )
506 + char* attronly[] = { "sOARecord", NULL };
509 + // search for SOARecord of domain
510 + filter = "(&(associatedDomain=" + toLower( m_pldap->escape( domain ) ) + ")(SOARecord=*))";
511 + m_msgid = m_pldap->search( getArg( "basedn" ), LDAP_SCOPE_SUBTREE, filter, (const char**) attronly );
512 + m_pldap->getSearchEntry( m_msgid, m_result );
514 + if( m_result.count( "sOARecord" ) && !m_result["sOARecord"].empty() )
517 + fillSOAData( m_result["sOARecord"][0], sd );
520 + di.serial = sd.serial;
524 + di.kind = DomainInfo::Master;
536 +class LdapFactory : public BackendFactory
541 + LdapFactory() : BackendFactory( "ldap" ) {}
544 + void declareArguments( const string &suffix="" )
546 + declare( suffix, "host", "One or more LDAP server with ports or LDAP URIs (separated by spaces)","ldap://127.0.0.1:389/" );
547 + declare( suffix, "starttls", "Use TLS to encrypt connection (unused for LDAP URIs)", "no" );
548 + declare( suffix, "basedn", "Search root in ldap tree (must be set)","" );
549 + declare( suffix, "binddn", "User dn for non anonymous binds","" );
550 + declare( suffix, "secret", "User password for non anonymous binds", "" );
551 + declare( suffix, "method", "How to search entries (simple, strict or tree)", "simple" );
552 + declare( suffix, "filter-axfr", "LDAP filter for limiting AXFR results", "(:target:)" );
553 + declare( suffix, "filter-lookup", "LDAP filter for limiting IP or name lookups", "(:target:)" );
554 + declare( suffix, "disable-ptrrecord", "Deprecated, use ldap-method=strict instead", "no" );
558 + DNSBackend* make( const string &suffix="" )
560 + return new LdapBackend( suffix );
570 + LdapFactory factory;
576 + BackendMakers().report( &factory );
577 + L << Logger::Info << " [LdapBackend] This is the ldap module version "VERSION" ("__DATE__", "__TIME__") reporting" << endl;
582 +static LdapLoader ldaploader;
583 diff -Nur pdns-2.9.21.1.orig/modules/ldapbackend/powerldap.cc pdns-2.9.21.1.int16/modules/ldapbackend/powerldap.cc
584 --- pdns-2.9.21.1.orig/modules/ldapbackend/powerldap.cc 2007-04-21 07:56:36.000000000 -0600
585 +++ pdns-2.9.21.1.int16/modules/ldapbackend/powerldap.cc 2008-10-06 10:08:55.000000000 -0600
590 -PowerLDAP::PowerLDAP( const string& hosts, uint16_t port, bool tls )
591 +PowerLDAP::PowerLDAP( const string& hosts, u_int16_t port, bool tls )
593 int protocol = LDAP_VERSION3;
595 diff -Nur pdns-2.9.21.1.orig/modules/ldapbackend/powerldap.hh pdns-2.9.21.1.int16/modules/ldapbackend/powerldap.hh
596 --- pdns-2.9.21.1.orig/modules/ldapbackend/powerldap.hh 2007-04-21 07:56:36.000000000 -0600
597 +++ pdns-2.9.21.1.int16/modules/ldapbackend/powerldap.hh 2008-10-06 10:11:53.000000000 -0600
602 -#ifdef HAVE_STDINT_H
605 #include <sys/types.h>
612 typedef map<string, vector<string> > sentry_t;
613 typedef vector<sentry_t> sresult_t;
615 - PowerLDAP( const string& hosts = "ldap://127.0.0.1/", uint16_t port = LDAP_PORT, bool tls = false );
616 + PowerLDAP( const string& hosts = "ldap://127.0.0.1/", u_int16_t port = LDAP_PORT, bool tls = false );
619 void getOption( int option, int* value );
620 diff -Nur pdns-2.9.21.1.orig/modules/ldapbackend/powerldap.hh.orig pdns-2.9.21.1.int16/modules/ldapbackend/powerldap.hh.orig
621 --- pdns-2.9.21.1.orig/modules/ldapbackend/powerldap.hh.orig 1969-12-31 17:00:00.000000000 -0700
622 +++ pdns-2.9.21.1.int16/modules/ldapbackend/powerldap.hh.orig 2007-04-21 07:56:36.000000000 -0600
625 + * PowerDNS LDAP Connector
626 + * By PowerDNS.COM BV
627 + * By Norbert Sendetzky <norbert@linuxnetworks.de> (2003-2007)
629 + * This program is free software; you can redistribute it and/or modify
630 + * it under the terms of the GNU General Public License version 2
631 + * as published by the Free Software Foundation.
633 + * This program is distributed in the hope that it will be useful,
634 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
635 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
636 + * GNU General Public License for more details.
638 + * You should have received a copy of the GNU General Public License
639 + * along with this program; if not, write to the Free Software
640 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
648 +#include <exception>
649 +#include <stdexcept>
654 +#ifdef HAVE_CONFIG_H
658 +#ifdef HAVE_STDINT_H
661 +#include <sys/types.h>
666 +#ifndef POWERLDAP_HH
667 +#define POWERLDAP_HH
677 +class LDAPException : public std::runtime_error
680 + explicit LDAPException( const string &str ) : std::runtime_error( str ) {}
685 +class LDAPTimeout : public LDAPException
688 + explicit LDAPTimeout() : LDAPException( "Timeout" ) {}
697 + const string getError( int rc = -1 );
698 + int waitResult( int msgid = LDAP_RES_ANY, int timeout = 0, LDAPMessage** result = NULL );
701 + typedef map<string, vector<string> > sentry_t;
702 + typedef vector<sentry_t> sresult_t;
704 + PowerLDAP( const string& hosts = "ldap://127.0.0.1/", uint16_t port = LDAP_PORT, bool tls = false );
707 + void getOption( int option, int* value );
708 + void setOption( int option, int value );
710 + void simpleBind( const string& ldapbinddn = "", const string& ldapsecret = "" );
711 + int search( const string& base, int scope, const string& filter, const char** attr = 0 );
713 + bool getSearchEntry( int msgid, sentry_t& entry, bool dn = false, int timeout = 5 );
714 + void getSearchResults( int msgid, sresult_t& result, bool dn = false, int timeout = 5 );
716 + static const string escape( const string& tobe );