|  | import logging | 
        
          |  | import traceback | 
        
          |  | import pdns.remotebackend | 
        
          |  | import ldap | 
        
          |  |  | 
        
          |  | TTL = 300 | 
        
          |  | LDAP_URI = "ldap://ldap.hashbang.sh" | 
        
          |  | CA_STORE = "/etc/ssl/certs/ca-certificates.crt" | 
        
          |  | DOMAIN = "hashbang.sh" | 
        
          |  | SOA = "ns.hashbang.sh. hostmaster.hashbang.sh. 1432461311 10800 3600 604800 1800" | 
        
          |  | logging.basicConfig(filename='pdns-hashbang.log',level=logging.DEBUG) | 
        
          |  |  | 
        
          |  | class HashbangHandler(pdns.remotebackend.Handler): | 
        
          |  | def __init__(self): | 
        
          |  | pdns.remotebackend.Handler.__init__(self) | 
        
          |  |  | 
        
          |  | self.ldapConnection = self.connect_to_ldap() | 
        
          |  | self.userCache = {} | 
        
          |  | self.hostCache = {} | 
        
          |  |  | 
        
          |  | def connect_to_ldap(self): | 
        
          |  | ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, CA_STORE) | 
        
          |  | connection = ldap.ldapobject.ReconnectLDAPObject(LDAP_URI, retry_max=10, retry_delay=5) | 
        
          |  | connection.set_option(ldap.OPT_X_TLS_DEMAND, True) | 
        
          |  | connection.start_tls_s() | 
        
          |  | connection.simple_bind_s() | 
        
          |  |  | 
        
          |  | logging.info("Connected to LDAP") | 
        
          |  |  | 
        
          |  | return connection | 
        
          |  |  | 
        
          |  | def lookup_ip_for_user(self, uid): | 
        
          |  | try: | 
        
          |  | if uid in self.userCache: | 
        
          |  | host = self.userCache[uid] | 
        
          |  | else: | 
        
          |  | host = self.lookup_field_in_ldap("uid=%s,ou=People,dc=hashbang,dc=sh" % uid, "host") | 
        
          |  | self.userCache[uid] = host | 
        
          |  |  | 
        
          |  | logging.debug("%s is on host %s" % (uid, host)) | 
        
          |  |  | 
        
          |  | if host in self.hostCache: | 
        
          |  | ip = self.hostCache[host] | 
        
          |  | else: | 
        
          |  | ip = self.lookup_field_in_ldap("cn=%s,ou=Servers,dc=hashbang,dc=sh" % host, "ipHostNumber") | 
        
          |  | self.hostCache[host] = ip | 
        
          |  |  | 
        
          |  | logging.debug("%s has the IP %s" % (host, ip)) | 
        
          |  |  | 
        
          |  | return ip | 
        
          |  | except ldap.NO_SUCH_OBJECT: | 
        
          |  | self.log.append("No host or IP for %s" % uid) | 
        
          |  | logging.debug("No host or IP for user %s" % uid) | 
        
          |  | except (KeyboardInterrupt, SystemExit): | 
        
          |  | raise | 
        
          |  | except: | 
        
          |  | self.log.append("Failed to process request for user %s" % uid) | 
        
          |  | logging.exception("Failed to lookup ip for user %s" % uid) | 
        
          |  |  | 
        
          |  |  | 
        
          |  | def lookup_field_in_ldap(self, dn, key): | 
        
          |  | result = self.ldapConnection.search_s(dn, ldap.SCOPE_BASE, "(objectClass=*)", (key,)) | 
        
          |  | return result[0][1][key][0] | 
        
          |  |  | 
        
          |  | def lookup_all_users(self): | 
        
          |  | result = self.ldapConnection.search_s("ou=People,dc=hashbang,dc=sh", | 
        
          |  | ldap.SCOPE_ONELEVEL, '(objectClass=*)', ("uid","host"), 0) | 
        
          |  | entries = [] | 
        
          |  | for entry in result: | 
        
          |  | uid = entry[1]["uid"][0] | 
        
          |  | host = entry[1]["host"][0] | 
        
          |  | self.userCache[uid] = host | 
        
          |  | ip = self.lookup_ip_for_user(uid) | 
        
          |  | if ip: | 
        
          |  | entries.append({"uid": uid, "host": host, "ip": ip}) | 
        
          |  |  | 
        
          |  | return entries | 
        
          |  |  | 
        
          |  | def do_lookup(self, args): | 
        
          |  | logging.debug("Got request for %s %s" %(args['qname'], args['qtype'])) | 
        
          |  |  | 
        
          |  | if args["qtype"] not in ["ANY", "A", "SOA"]: | 
        
          |  | return | 
        
          |  |  | 
        
          |  | qname = args["qname"].split(".", 1) | 
        
          |  | if len(qname) != 2: | 
        
          |  | return | 
        
          |  |  | 
        
          |  | subdomain, domain = qname | 
        
          |  | if domain != DOMAIN and args["qname"] != DOMAIN: | 
        
          |  | return | 
        
          |  |  | 
        
          |  | if args["qtype"] == "SOA": | 
        
          |  | record = self.record_prio_ttl(DOMAIN, "SOA", SOA, 0, TTL) | 
        
          |  | else: | 
        
          |  | if subdomain == "*" or domain == "sh": | 
        
          |  | return | 
        
          |  |  | 
        
          |  | ip = self.lookup_ip_for_user(subdomain) | 
        
          |  | if ip: | 
        
          |  | record = self.record_prio_ttl(args['qname'], "A", ip, 0, TTL) | 
        
          |  | else: | 
        
          |  | return | 
        
          |  |  | 
        
          |  | self.result = [record] | 
        
          |  | logging.debug(record) | 
        
          |  |  | 
        
          |  | def do_list(self, args): | 
        
          |  | logging.info("Got AXFR for %s" % args["zonename"]) | 
        
          |  |  | 
        
          |  | if args["zonename"] != DOMAIN: | 
        
          |  | return | 
        
          |  |  | 
        
          |  | self.result = [self.record_prio_ttl("%s.%s" % (result["uid"], DOMAIN), "A", result["ip"], 0, TTL) | 
        
          |  | for result in self.lookup_all_users()] | 
        
          |  |  | 
        
          |  | logging.debug("AXFR reply %s" % self.result) | 
        
          |  |  | 
        
          |  | if __name__ == '__main__': | 
        
          |  | try: | 
        
          |  | pdns.remotebackend.PipeConnector(HashbangHandler).run() | 
        
          |  | except: | 
        
          |  | logging.exception("Crash") | 
        
          |  | raise |