[Pdns-users] Attempting to use single pipe-backend process

Chris Foote chris at inetd.com.au
Fri Oct 20 12:14:43 UTC 2006


I have a problem with getting PowerDNS to only use a single instance
of a pipe-backend process - it always runs a second copy after PowerDNS
gets its first DNS query:

enum1:/etc/powerdns/pdns.d# invoke-rc.d pdns monitor
Oct 20 21:58:52 [PIPEBackend] This is the pipebackend version 2.9.20 (Sep  9 2006, 17:27:05) reporting
Oct 20 21:58:52 This is a standalone pdns
Oct 20 21:58:52 It is advised to bind to explicit addresses with the --local-address option
Oct 20 21:58:52 UDP server bound to 0.0.0.0:53
Oct 20 21:58:52 PowerDNS 2.9.20 (C) 2001-2006 PowerDNS.COM BV (Sep  9 2006, 17:34:31, gcc 3.3.5 (Debian 1:3.3.5-13)) starting up
Oct 20 21:58:52 PowerDNS comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it according to the terms of the GPL version 2.
Oct 20 21:58:52 Set effective group id to 106
Oct 20 21:58:52 Set effective user id to 104
Oct 20 21:58:52 About to create 1 backend threads for UDP
% Oct 20 21:58:52 Backend launched with banner: OK      Python backend firing up
Oct 20 21:58:53 Done launching threads, ready to distribute questions
************[ DNS query performed here, which is when the 2nd process starts up ]
Oct 20 22:00:15 Engaging bypass - now operating unthreaded
Oct 20 22:00:15 Backend launched with banner: OK        Python backend firing up
Oct 20 22:00:15 Query: 'Q       www.example.com IN      ANY     -1      127.0.0.1'
Oct 20 22:00:15 Query: 'Q       webserver.example.com   IN      ANY     -1      127.0.0.1'

I can confirm that the first backend process (Python script attached)
never sees the query, and pdns sends the query to the second process
after starting it up (why ?...)

I upgraded to powerdns 2.9.20 from 2.9.17 hoping that the recent patch
to the CoProcess code[1] might have fixed it, but not for my case.

The config I'm using is:

allow-recursion=127.0.0.3
config-dir=/etc/powerdns
daemon=yes
disable-axfr=yes
guardian=yes
lazy-recursion=yes
local-address=0.0.0.0
local-port=53
module-dir=/usr/lib/powerdns
setgid=pdns
setuid=pdns
socket-dir=/var/run
version-string=powerdns
include=/etc/powerdns/pdns.d
cache-ttl=0
disable-tcp=yes
distributor-threads=1
launch=pipe
logfile=/var/log/pdns.log
loglevel=9
negquery-cache-ttl=0
query-cache-ttl=0
use-logfile=yes
query-logging=yes
pipe-command=/tmp/dummy-backend.py


Does anyone have a hint for allowing just a single process to run ?

Best regards,
Chris

[1] http://wiki.powerdns.com/projects/trac/changeset/525
-------------- next part --------------
#!/usr/bin/python2.4

"""Test Python process for PowerDNS pipe backend."""

import sys, os, time

class DNSLookup(object):
    """Handle PowerDNS pipe-backend domain name lookups."""
    ttl = 1
    
    def __init__(self, query):
        """parse DNS query and produce lookup result.

        query: a sequence containing the DNS query as per PowerDNS manual appendix A:
        http://downloads.powerdns.com/documentation/html/backends-detail.html#PIPEBACKEND-PROTOCOL
        """
        (_type, qname, qclass, qtype, _id, ip) = query
        self.has_result = False  # has a DNS query response
        qname_lower = qname.lower()

        self.results = []
        
        if (qtype == 'A' or qtype == 'ANY') and qname_lower == 'webserver.example.com':
            self.results.append('DATA\t%s\t%s\tA\t%d\t-1\t1.2.3.4' %
                                (qname, qclass, DNSLookup.ttl))
            self.results.append('DATA\t%s\t%s\tA\t%d\t-1\t1.2.3.5' %
                                (qname, qclass, DNSLookup.ttl))
            self.results.append('DATA\t%s\t%s\tA\t%d\t-1\t1.2.3.6' %
                                (qname, qclass, DNSLookup.ttl))
            self.has_result = True
        elif (qtype == 'CNAME' or qtype == 'ANY') and qname_lower == 'www.example.com':
            self.results.append('DATA\t%s\t%s\tCNAME\t%d\t-1\twebserver.example.com' %
                                (qname, qclass, DNSLookup.ttl))
            self.has_result = True

    def str_result(self):
        """return string result suitable for pipe-backend output to PowerDNS."""
        if self.has_result:
            return '\n'.join(self.results)
        else:
            return ''

class Logger(object):
    def __init__(self):
        pid = os.getpid()
        self.logfile = '/tmp/backend-%d.log' % pid

    def write(self, msg):
        logline = '%s|%s\n' % (time.asctime(), msg)
        f = file(self.logfile, 'a')
        f.write(logline)
        f.close()

def debug_log(msg):
    logger.write(msg)

class PowerDNSbackend(object):
    """The main PowerDNS pipe backend process."""
    
    def __init__(self, filein, fileout):
        """initialise and run PowerDNS pipe backend process."""
        self.filein = filein
        self.fileout = fileout
        
        self._process_requests()   # main program loop

    def _process_requests(self):
        """THE main program loop."""
        first_time = True
        while 1:
            rawline = self.filein.readline()
            if rawline == '':
                debug_log('EOF')
                return  # EOF detected
            line = rawline.rstrip()

            debug_log('received from pdns:%s' % line)
            
            if first_time:
                if line == 'HELO\t1':
                    self._fprint('OK\tPython backend firing up')
                else:
                    self._fprint('FAIL')
                    debug_log('HELO input not received - execution aborted')
                    rawline = self.filein.readline()  # as per docs - read another line before aborting
                    debug_log('calling sys.exit()')
                    sys.exit(1)
                first_time = False
            else:
                query = line.split('\t')
                if len(query) != 6:
                    self._fprint('LOG\tPowerDNS sent unparseable line')
                    self._fprint('FAIL')
                else:
                    debug_log('Performing DNSLookup(%s)' % repr(query))
                    lookup = DNSLookup(query)
                    if lookup.has_result:
                        self._fprint(lookup.str_result())
                    self._fprint('END')

    def _fprint(self, message):
        """Print the given message with newline and flushing."""
        self.fileout.write(message + '\n')
        self.fileout.flush()
        debug_log('sent to pdns:%s' % message)

if __name__ == '__main__':
    logger = Logger()
    infile = sys.stdin
    #sys.stdout.close()
    #outfile = os.fdopen(1, 'w', 1)
    outfile = sys.stdout
    try:
        PowerDNSbackend(infile, outfile)
    except:
        debug_log('execution failure:' + str(sys.exc_info()[0]))
        raise


More information about the Pdns-users mailing list