[Pdns-users] Pipe Backend causes Signal 6

Steve Chapman swchapman at att.com
Mon Feb 20 20:53:08 UTC 2006


I am trying to make PowerDNS a "traffic cop" of sorts.  I have made PowerDNS
"authoritative" for my parent corporation because some of the services at my
parent corporation are accessible via an internal private route, and some
are only accessible if my company goes there via the Internet.  I wrote a
perl script that is based heavily on the sample provided in the
documentation, but also modified to route dns requests to either an internal
server or the Internet, based on the hostname.  It also records the results
in case the main corporate DNS server is no longer accessible, then I can
restart PowerDNS in bind mode with the recorded results as the zone file.

However, if someone tries to access a host that doesn't exist in the
corporate domain, the pipe script causes a Signal 6 in the pdns server (if I
have distributor-threads=1) that loops infinitely, or else (if
distributor-threads>1) respawns processes until the server runs out of file
descriptors.

I'm pretty sure it's my script and the weird workaround I'm trying to do
that is causing issues.  Am I missing some sort of exit clause that states
"If you don't find a response for a request for which you should be
authoritative, exit normally"? Or am I doing something with PDNS that is so
objectionable that it's vomiting at me?

I am using pdns 2.9.19-1 that I installed from the rpm on a Linux 7.2
machine, perl 5.6.1.

Here is my script:

------- SCRIPT STARTS -------
#!/usr/bin/perl -w
# PowerDNS Coprocess backend
#
# This backend script pretends to be authoritative
# for the domain [corp].com, where really all it does
# is check the name you are looking up against a
# list of files in $inet_sitelist.  If it finds a
# match, it resolves the address using a public
# Internet DNS Server, otherwise it forwards the
# request to the internal corporate DNS server.

use Net::DNS;
use strict;

print @_;
$|=1;                                   # no buffering

my $debugvar = 0; # set to 1 to log debug messages
                  # to /var/log/messages

sub debug_comment {
       my $textstr = shift;
       if ($debugvar) {
               system("logger -p local3.info $textstr");
       }
       return 0;
}

sub list_data {
        my $listfile = shift;
        open DATA, "$listfile" or die;
        my @datalist = <DATA>;
        close (DATA);
        return @datalist;
}

# $inetdnssrv is the public Internet server we use to resolve
# certain [corp] hostnames that are not accessible via Corp.
my $inetdnssrv = "[Public INTERNET SERVER]";

# $corpdnssrv is the corporate DNS proxy we use for most [corp] host
# name resolutions
my $corpdnssrv = "[Corporate Proxy Server]";

# $inet_sitelist contains the corporate hosts we need to resolve
# via the internet rather than the internal corporate DNS server
my $inet_sitelist = "/etc/powerdns/inetdns.list";

# $zonefile is the file to which we write all [corp].com results
# collected in case we have to switch pdns to the bind config
my $zonefile = "/var/named/pdns/corp-zonefile";

my $zfentry;
my @zfcheck;
my $zfappend;
my $item;

my $line=<>;
chomp($line);

unless($line eq "HELO\t1") {
        print "FAIL\n";
#       print STDERR "Received '$line'\n";
        <>;
        exit;
}
print "OK       Backend firing up\n";   # print our banner

# populate the inetdns array with the values from the inet_sitelist
my @inetdns = list_data($inet_sitelist);

# Init counter....
# Hack to limit the frequency with which the "external" data file
# is read.
my $counter = 0;
my $MAXCOUNT = 200;  # This is the number of iterations through
                     # the following loop before we pause to re-read
                     # the external data file

my $res = Net::DNS::Resolver->new(
        debug => 0,
        recursion =>1,
);

while(<>)
{
        # Default recursion DNS Server
        my $dnssrv = $corpdnssrv; # Corporate DNS Server
        print STDERR "$$ Received: $_";
        chomp();
        my @arr=split(/\t/);
        if(@arr<6) {
                print "LOG      PowerDNS sent unparseable line\n";
                print "FAIL\n";
                next;
        }

        if ($counter gt $MAXCOUNT) {
                debug_comment("Re-reading zone lists");
                # Re-read the external data file
                # For S&G, how many records are in each array?
                @inetdns = list_data($inet_sitelist);
                my $numinet = $#inetdns + 1;
                debug_comment("List items in inetdns: $numinet");
                # Reset the counter
                $counter = 0;
        }

        # Increment counter
        $counter++;

        my ($type,$qname,$qclass,$qtype,$aws,$id,$ip)=split(/\t/);
        debug_comment("Looking for $qname");

        sub write_zonefile {
                if ($qname =~ ".[corp].com") {
                        $zfappend = "no";
                        $zfentry = "$qname.\t$qclass\tA\t$aws";
                        @zfcheck = list_data($zonefile);
                        open ZDATA, "+>>$zonefile" or die;
                        foreach my $item2 (@zfcheck) {
                                if ($item2 eq "$zfentry\n") {
                                        $zfappend = "yes";
                                        debug_comment("$zfentry exists in
zonefile, not adding");
                                }
                        }
                        if ($zfappend eq "no") {
                                print ZDATA "$zfentry\n";
                                debug_comment("Adding $zfentry to
zonefile");
                        }
                        close (ZDATA);
                }
                return 0;
        }

        foreach $item (@inetdns) {
                # It is very important to include the carriage return
                # with $qname, as the carriage return is read into the
                # array when pulling the values from $site_inetlist
                if ($item eq "$qname\n") {
                        debug_comment("$qname is in inet list");
                        # Public Internet DNS server
                        $dnssrv = $inetdnssrv;
                }
        }

        if (($qtype eq "A" || $qtype eq "ANY") && $qname =~ ".[corp].com") {
                print STDERR "$$ Sent A records\n";
                debug_comment("Special name $qname... resolving with
$dnssrv");

                $res->nameservers($dnssrv);
                #print STDERR @nameservers;

                # Perform a lookup, using the searchlist if appropriate.
                my $query = $res->search($qname);

                foreach my $rr ($query->answer) {
                        next unless $rr->type eq "A";
                        $aws = $rr->address;
                        # Print the results to the screen
                        print
"DATA\t$qname\t$qclass\tA\t3600\t86400\t$aws\n";
                        # Write the results to the zone file
                        write_zonefile($qname, $qclass, $aws);
                        debug_comment("Address $aws");
                }
                debug_comment("Resolver done.");
        }
        print STDERR "$$ End of data\n";
        print "END\n";
}
-------- SCRIPT ENDS --------


Thank you in advance for any help/suggestions.

Steve Chapman
Network Engineer



More information about the Pdns-users mailing list