Source code for pdns.remotebackend

"""PowerDNS remotebackend support module"""
import json,sys,io,re,os

VERSION="0.6"
"""Module version string"""

[docs]class Handler: """Base class for request handler, you have to implement at least do_lookup. Please see http://doc.powerdns.com/html/remotebackend.html#remotebackend-api for information on how to implement anything. All methods get called with hash containing the request variables.""" def __init__(self): """Initialize with default values""" self.log = [] """Array of log messages""" self.result = False """Result of the invocation""" self.ttl = 300 """Default TTL""" self.params = {} """Any handler parameters you want to store"""
[docs] def record_prio_ttl(self, qname, qtype, content, prio, ttl, auth=1): """Generate one record without any defaults""" return {'qtype': qtype, 'qname': qname, 'content': content, 'ttl': ttl, 'auth': auth}
[docs] def record_prio(self, qname, qtype, content, prio, auth=1): """Generate one record with default ttl""" return self.record_prio_ttl(qname, qtype, content, prio, self.ttl, auth)
[docs] def record(self, qname, qtype, content, auth=1): """Generate one record with default ttl and prio (0)""" return self.record_prio(qname, qtype, content, 0, auth)
[docs] def do_initialize(self, *args): """Default handler for initialization method, stores any parameters into attribute params""" self.params = args self.log.append("PowerDNS python remotebackend version {0} initialized".format(VERSION)) self.result = True
[docs]class Connector: """Connector base class for handling endless loop""" def __init__(self, klass, options = {}): self.handler = klass # initialize the handler class """"Handler class""" self.options = options """Any options""" if ("abi" in self.options) == False: self.options["abi"] = 'remote'
[docs] def mainloop(self, reader, writer): """Setup basic reader/writer and start correct main loop""" h = self.handler() if 'ttl' in self.options: h.ttl = self.options['ttl'] if self.options["abi"] == 'pipe': return self.mainloop3(reader, writer, h) else: return self.mainloop4(reader, writer, h)
[docs] def mainloop3(self, reader, writer, h): """Reader/writer and request de/serialization for pipe backend""" h = self.handler() if 'ttl' in self.options: h.ttl = self.options['ttl'] # initialize line = reader.readline() m = re.match("^HELO\t([1-4])", line) if m != None: # simulate empty initialize h.do_initialize({}) writer.write("OK\tPowerDNS python remotebackend version {0} initialized\n".format(VERSION)) writer.flush() self.abi = int(m.group(1)) else: writer.write("FAIL\n"); writer.flush() while(True): line = reader.readline() if line == "": return # keep track of last seen SOA for AXFR last_soa_name = None while(True): line = reader.readline().strip().split("\t") if not line: break if (len(line) < 2): break h.log = [] h.result = False method = line[0] parms = {} if method == "AXFR": parms = {"zonename":last_soa_name, "domain_id":int(line[1])} method = "do_list" elif method == "Q": parms = {"qname":line[1], "qclass":line[2], "qtype":line[3], "domain_id":int(line[4]), "zone_id":int(line[4]), "remote":line[5]} if self.abi > 1: parms["local"] = line[6] if self.abi > 2: parms["edns-subnet"] = line[7] if (line[3] == "SOA"): last_soa_name = line[1] method = "do_lookup" else: writer.write("FAIL\n") writer.flush() continue if (callable(getattr(h, method, None))): getattr(h,method)(parms) if (len(h.log) > 0): writer.write("LOG\t{0}\n".format(h.log[0])) if (h.result != False): for r in h.result: if ("scopeMask" in r) == False: r["scopeMask"] = 0 if ("domain_id" in r) == False: r["domain_id"] = int(parms["domain_id"]) if (self.abi < 3): writer.write("DATA\t{0}\t{1}\t{2}\t{3}\t{4}\t{5}\n".format(r["qname"], "IN", r["qtype"], r["ttl"], r["domain_id"], r["content"])) else: writer.write("DATA\t{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\t{7}\n".format(r["scopeMask"], r["auth"], r["qname"], "IN", r["qtype"], r["ttl"], r["domain_id"], r["content"])) writer.write("END\n") else: writer.write("FAIL\n") writer.flush()
[docs] def mainloop4(self, reader, writer, h): """Reader/writer and request de/serialization for remotebackend""" while(True): line = reader.readline() if line == "": break try: data_in = json.loads(line) method = "do_{0}".format(data_in['method'].lower()) args = {} if ('parameters' in data_in): args = data_in['parameters'] h.result = False h.log = [] if (callable(getattr(h, method, None))): getattr(h,method)(args) writer.write(json.dumps({'result':h.result,'log':h.log})) except ValueError: writer.write(json.dumps({'result':False,'log':"Cannot parse input"})) writer.write("\n") writer.flush()
[docs]class PipeConnector(Connector): """Pipe Connector"""
[docs] def run(self): """Startup pipe connector with stdin/stdout as source/sink""" try: self.mainloop(sys.stdin, sys.stdout) except (KeyboardInterrupt, SystemExit): pass