<?
/*
 * This code is released into the public domain.  Feel free to use it and
 * distribute it however you wish.
 *
 * Eric Kilfoil <eric@ypass.net>
 *
 */

if (defined("DNS_INC"))
    return(
"");
define("DNS_INC""1");

/*******************************************************************

For information on configuring this class, visit
http://www.ypass.net/php/dns/

*******************************************************************/

define("NAMED_CONF_PATH""/var/named/named.conf");
define("NAMED_CONF_MASTER_ADD_FILE""/var/named/named.masters");
define("NAMED_CONF_SLAVE_ADD_FILE""/var/named/named.masters");
define("NAMED_CONF_PATH_PARSED""/var/named/named.conf.parsed");
define("NAMED_DIR""/var/named");
define("LOCK_DIR""/var/lock/");

// Default values for new zones (DON'T FORGET THE TRAILING DOTS!!!!!)
define("DEFAULT_PRIMARY_NAMESERVER""ns1.yourdomain.com.");
define("DEFAULT_SECONDARY_NAMESERVER""ns2.yourdomain.com.");
define("DEFAULT_CONTACT_EMAIL""hostmaster.yourdomain.com.");
define("DEFAULT_PRIMARY_MX""mx1.yourdomain.com.");
define("DEFAULT_SECONDARY_MX""mx2.yourdomain.com.");

class 
ZONE {
    var 
$domainName;
    var 
$defaultTTL "";
    var 
$origin;
    var 
$rr = array();
    var 
$zoneconf;
    var 
$error;
    var 
$curRR 0;

    function 
ZONE($domainName$search 0)
    {
        
$this->zoneconf = new ZONECONF;
        if (
$domainName != "")
        {
            
$this->domainName $domainName;
            
$this->origin $domainName ".";
            if (! 
$this->zoneconf->findZoneFile($domainName))
                
$this->error $this->zoneconf->error;
            else
                
$this->readZone();
        }
    }

    
// Locks the zone file so other's can't access it
    
function lock($domainName)
    {
        if (
file_exists(LOCK_DIR "/" $domainName ".lock"))
        {
            
$this->error "$domainName is already locked";
            return(
0);
        }

        if (
touch(LOCK_DIR "/" $domainName ".lock"))
            return(
1);

        
$this->error "couldn't touch lock file";
        return(
0);
    }

    
// Unlocks the zone file
    
function unlock($domainName)
    {
        if (
unlink(LOCK_DIR "/" $domainName ".lock"))
            return(
1);
        
$this->error "could not unlock domain";
        return(
0);
    }

    
// Tests to see if the zone is locked
    
function isLocked($domainName)
    {
        if (
file_exists(LOCK_DIR "/" $domainName ".lock"))
            return(
1);
        return(
0);
    }

    
// Open's and parses the zone file for the domain.
    // An RR object is created for each resource record for the zone.
    // GENERATE and TTL are parsed correctly.
    
function readZone()
    {
        if (! (
$f fopen(NAMED_DIR "/" $this->zoneconf->file"r")))
        {
            
$this->error "Couldn't open " NAMED_DIR "/" $this->zoneconf->file " for read";
            return(
0);
        }

        
$foundSOA 0;
        
$rrnum 0;
        
$linenum 0;
        while (! 
feof($f))
        {
            
$line fgets($f1024);
            if (
ereg("^[ \t]*;"$line$regs))
                continue;
            if (
ereg("^[ \t\n\r]$"$line$regs))
                continue;

            
// Parse any $ commands
            
if (ereg("^\\\$([^ \t]+)[ \t]+(.*)$"$line$regs))
            {
                switch (
$regs[1])
                {
                    case 
"ORIGIN":
                        
$this->origin chop($regs[2]);
                        break;
                    case 
"TTL":
                        
$this->defaultTTL chop($regs[2]);
                        break;
                    case 
"GENERATE":
                        if (
ereg("^([0-9]+)-([0-9]+)[ \t]+([^ \t]+)[ \t]+([^ \t]+)[ \t]+([^ \t\r\n]+)"$regs[2], $gen))
                        {
                            for (
$ctr $gen[1]; $ctr <= $gen[2]; $ctr++)
                            {
                                
ereg("(.*)\\\$(.*)"$gen[3], $lhs);
                                
$gen[3] = $lhs[1] . $ctr $lhs[2];
                                
ereg("(.*)\\\$(.*)"$gen[5], $rhs);
                                
$gen[5] = $rhs[1] . $ctr $rhs[2];
                                
$line .= $gen[3] . "\t" $gen[4] . "\t" $gen[5] . "\n";

                                
$this->rr[$rrnum]->lhs $gen[3];
                                
$this->rr[$rrnum]->id $rrnum;
                                
$this->rr[$rrnum]->ttl "";
                                
$this->rr[$rrnum]->type $gen[4];
                                switch (
$this->rr[$rrnum]->type)
                                {
                                    case 
"A":
                                        
$this->rr[$rrnum]->rhs = new A;
                                        
$this->rr[$rrnum++]->rhs->ipAddress $gen[5];
                                        break;
                                    case 
"AAAA":
                                        
$this->error "parse error: can't handle IPv6 yet";
                                        return(
0);
                                    case 
"CNAME":
                                        
$this->rr[$rrnum]->rhs = new CNAME;
                                        
$this->rr[$rrnum++]->rhs->hostname $gen[5];
                                        break;
                                    case 
"MX":
                                        if (!
ereg("^([0-9]+)[ \t]+([^ \t;]+)"$gen[5], $mx))
                                        {
                                            
$this->error "bad mx record on line $linenum";
                                            return(
0);
                                        }
                                        
$this->rr[$rrnum]->rhs = new MX;
                                        
$this->rr[$rrnum]->rhs->preference $mx[1];
                                        
$this->rr[$rrnum++]->rhs->hostname $mx[2];
                                        break;
                                    case 
"NS":
                                        
$this->rr[$rrnum]->rhs = new NS;
                                        
$this->rr[$rrnum++]->rhs->hostname $gen[5];
                                        break;
                                    case 
"PTR":
                                        
$this->rr[$rrnum]->rhs = new PTR;
                                        
$this->rr[$rrnum++]->rhs->hostname $gen[5];
                                        break;
                                }
                                if (
substr($this->rr[$rrnum-1]->lhsstrlen($this->rr[$rrnum-1]->lhs)-1) != ".")
                                    
$this->rr[$rrnum-1]->lhs $this->rr[$rrnum-1]->lhs "." $this->origin;
                            }
                        }
                        break;
                }
                continue;
            }

            if (! 
$foundSOA)
            {
                
// Get the whole SOA record before doing anything else
                
$line ereg_replace("^(.*)[;]*.*$""\\1"$line);
                
$linenum++;
                while (!
strstr($line")"))
                {
                    
$line .= fgets($f1024);
                    
$linenum++;
                    
$line ereg_replace("^(.*);.*$""\\1"$line);
                }
                if (!
ereg("^([^ \t\n\r]+)[ \t\n\r]+IN[ \t\n\r]+SOA[ \t\n\r]+([^ \t\n\r]+)[ \t\n\r]+([^ \t\n\r]+)[ \t\n\r]+\([ \t\n\r]+([0-9]+)[ \t\n\r]+([0-9]+)[ \t\n\r]+([0-9]+)[ \t\n\r]+([0-9]+)[ \t\n\r]+([0-9]+)[ \t\n\r]*\)"$line$soa))
                {
                    
$this->error =  "SOA parser error on line $linenum";
                    return(
0);
                }
                
$this->rr[$rrnum] = new RR;
                
$this->rr[$rrnum]->id $rrnum;
                
$this->rr[$rrnum]->lhs $soa[1];
                
$this->rr[$rrnum]->ttl "";
                
$this->rr[$rrnum]->type "SOA";
                
$this->rr[$rrnum]->rhs = new SOA;
                
$this->rr[$rrnum]->rhs->priNS $soa[2];
                
$this->rr[$rrnum]->rhs->techContact $soa[3];
                
$this->rr[$rrnum]->rhs->serial $soa[4];
                
$this->rr[$rrnum]->rhs->refresh $soa[5];
                
$this->rr[$rrnum]->rhs->retry $soa[6];
                
$this->rr[$rrnum]->rhs->expire $soa[7];
                
$this->rr[$rrnum++]->rhs->minimum $soa[8];
                
$foundSOA 1;
            } else if (
ereg("^([^ \t]*)[ \t]+([0-9]*)[ \t]*[I]*[N]*[ \t]+(A|CNAME|MX|NS|PTR)[ \t]+([^;\n]+)"$line$regs)) {
                if (
$regs[1] == "")
                    
$this->rr[$rrnum]->lhs $this->rr[$rrnum-1]->lhs;
                else
                    
$this->rr[$rrnum]->lhs $regs[1];
                
$this->rr[$rrnum]->id $rrnum;
                
$this->rr[$rrnum]->ttl $regs[2];
                
$this->rr[$rrnum]->type $regs[3];
                switch (
$this->rr[$rrnum]->type)
                {
                    case 
"A":
                        
$this->rr[$rrnum]->rhs = new A;
                        
$this->rr[$rrnum++]->rhs->ipAddress $regs[4];
                        break;
                    case 
"AAAA":
                        
$this->error "parse error: can't handle IPv6 yet";
                        return(
0);
                    case 
"CNAME":
                        
$this->rr[$rrnum]->rhs = new CNAME;
                        
$this->rr[$rrnum++]->rhs->hostname $regs[4];
                        break;
                    case 
"MX":
                        if (!
ereg("^([0-9]+)[ \t]+([^ \t;]+)"$regs[4], $mx))
                        {
                            
$this->error "bad mx record on line $linenum";
                            return(
0);
                        }
                        
$this->rr[$rrnum]->rhs = new MX;
                        
$this->rr[$rrnum]->rhs->preference $mx[1];
                        
$this->rr[$rrnum++]->rhs->hostname $mx[2];
                        break;
                    case 
"NS":
                        
$this->rr[$rrnum]->rhs = new NS;
                        
$this->rr[$rrnum++]->rhs->hostname $regs[4];
                        break;
                    case 
"PTR":
                        
$this->rr[$rrnum]->rhs = new PTR;
                        
$this->rr[$rrnum++]->rhs->hostname $regs[4];
                        break;
                }
            } else if (! 
feof($f)) {
                
// Found an invalid RR
                
$this->error "RR parse error at line $linenum$line";
                return(
0);
            }
            
// Tack the origin on to the end of the lhs if needed
            
if ($this->rr[$rrnum-1]->lhs == "@")
                
$this->rr[$rrnum-1]->lhs $this->origin;
            else if (
substr($this->rr[$rrnum-1]->lhsstrlen($this->rr[$rrnum-1]->lhs)-1) != ".")
                
$this->rr[$rrnum-1]->lhs $this->rr[$rrnum-1]->lhs "." $this->origin;

            
// Tack the origin on the the end of the rhs if needed
            
switch ($this->rr[$rrnum-1]->type)
            {
                case 
"PTR":
                case 
"NS":
                case 
"MX":
                    if (
$this->rr[$rrnum-1]->rhs->hostname == "@")
                        
$this->rr[$rrnum-1]->rhs->hostname $this->origin;
                    if (
substr($this->rr[$rrnum-1]->rhs->hostnamestrlen($this->rr[$rrnum-1]->rhs->hostname)-1) != ".")
                        
$this->rr[$rrnum-1]->rhs->hostname $this->rr[$rrnum-1]->rhs->hostname "." $this->origin;
                default:
                    break;
            }
        }
        return(
1);
    }

    
// returns the name of the technical contact specified in the zone file
    
function administrator($admin "")
    {
        if (! 
$administrator)
            return(
$this->rr[0]->rhs->techContact);
        return(
$this->rr[0]->rhs->techContact $admin);
    }

    
// Returns the number of RR's in a zone
    
function rrCount()
    {
        return(
count($this->rr));
    }

    
// reset the internal RR counter to the first RR
    
function resetRR()
    {
        
$this->curRR 0;
    }

    
// get the next RR in the zone file
    
function getRR()
    {
        if (
$this->curRR count($this->rr))
            return(
0);
        return(
$this->rr[$this->curRR++]);
    }

    
// save any modifications back to the zone file
    
function saveZone()
    {
        
$this->resetRR();

        if (! 
$this->lock($this->domainName))
        {
            
$this->error "domain " $this->domainName " is current locked";
            return(
0);
        }

        if (! (
$f fopen(NAMED_DIR "/" $this->zoneconf->file ".new""w")))
        {
            
$this->error "Couldn't open " NAMED_DIR "/" $this->zoneconf->file ".new for write";
            return(
0);
        }

        
fputs($f'$TTL ' $this->defaultTTL "\n");
        while (
$rr $this->getRR())
        {
            
$writeRR $rr->lhs . ($rr->ttl ? ("    " $rr->ttl) : "") . "    IN " $rr->type "    ";
            switch(
$rr->type)
            {
                case 
"SOA":
                    
$rr->rhs->setSerial();
                    
$writeRR =  "@    " $rr->ttl " IN " $rr->type " " .
                                            
$rr->rhs->priNS "    " 
                                            
$rr->rhs->techContact " (\n\t\t" .
                                            
$rr->rhs->serial "\t; serial\n\t\t" .
                                            
$rr->rhs->refresh "\t\t; refresh\n\t\t" .
                                            
$rr->rhs->retry "\t\t; retry\n\t\t" .
                                            
$rr->rhs->expire "\t\t; expire\n\t\t" .
                                            
$rr->rhs->minimum " )\t\t; minimum\n\n";
                    break;
                case 
"NS":
                    
$writeRR .= $rr->rhs->hostname "\n";
                    break;
                case 
"MX":
                    
$writeRR .= $rr->rhs->preference "    " $rr->rhs->hostname "\n";
                    break;
                case 
"A":
                    
$writeRR .= $rr->rhs->ipAddress "\n";
                    break;
                case 
"PTR":
                    
$writeRR .= $rr->rhs->hostname "\n";
                    break;
                case 
"CNAME":
                    
$writeRR .= $rr->rhs->hostname "\n";
                    break;
                default:
                    
$this->error "unknown type $rr->type.";
                    
fclose($f);
                    
unlink(NAMED_DIR "/" $this->zoneconf->file ".new");
                    
$this->unlock($this->domainName);
                    return(
0);
            }
            
fputs($f$writeRR);
        }
        
fclose($f);
        
rename(NAMED_DIR "/" $this->zoneconf->file ".new"NAMED_DIR "/" $this->zoneconf->file);
        
$this->unlock($this->domainName);
        return(
1);
    }

    
// add a new resource record.  Only parameter is an RR object
    
function addRR($newRR)
    {
        if (
$newRR->lhs == "@")
            
$newRR->lhs $this->origin;
        if (
substr($newRR->lhsstrlen($newRR->lhs)-1) != ".")
            
$newRR->lhs $newRR->lhs "." $this->origin;

        if (
$newRR->rhs->hostname == "@")
            
$newRR->rhs->hostname $this->origin;
        if (
substr($newRR->rhs->hostnamestrlen($newRR->rhs->hostname)-1) != ".")
            
$newRR->rhs->hostname $newRR->rhs->hostname "." $this->origin;

        
$this->rr[$this->rrCount()] = $newRR;
    }

    
// add an MX record to the zone file (frontend for addRR)
    
function addMX($lhs$pref$rhs$ttl "")
    {
        
$newRR = new RR;
        
$newRR->lhs $lhs;
        
$newRR->ttl $ttl;
        
$newRR->type "MX";
        
$newRR->rhs = new MX;
        
$newRR->rhs->preference $pref;
        
$newRR->rhs->hostname $rhs;
        
$this->addRR($newRR);
    }

    
// add an NS record to the zone file (frontend for addRR)
    
function addNS($lhs$rhs$ttl "")
    {
        
$newRR = new RR;
        
$newRR->lhs $lhs;
        
$newRR->ttl $ttl;
        
$newRR->type "NS";
        
$newRR->rhs = new NS;
        
$newRR->rhs->hostname $rhs;
        
$this->addRR($newRR);
    }

    
// add an CNAME record to the zone file (frontend for addRR)
    
function addCNAME($lhs$rhs$ttl "")
    {
        
$newRR = new RR;
        
$newRR->lhs $lhs;
        
$newRR->ttl $ttl;
        
$newRR->type "CNAME";
        
$newRR->rhs = new CNAME;
        
$newRR->rhs->hostname $rhs;
        
$this->addRR($newRR);
    }

    
// add an A record to the zone file (frontend for addRR)
    
function addA($lhs$rhs$ttl "")
    {
        
$newRR = new RR;
        
$newRR->lhs $lhs;
        
$newRR->ttl $ttl;
        
$newRR->type "A";
        
$newRR->rhs = new A;
        
$newRR->rhs->ipAddress $rhs;
        
$this->addRR($newRR);
    }
    
    
// add an PTR record to the zone file (frontend for addRR)
    
function addPTR($lhs$rhs$ttl "")
    {
        
$newRR = new RR;
        
$newRR->lhs $lhs;
        
$newRR->ttl $ttl;
        
$newRR->type "PTR";
        
$newRR->rhs = new PTR;
        
$newRR->rhs->hostname $rhs;
        
$this->addRR($newRR);
    }

    
// remove an RR record from a zone file.  You can specify the specific
    // RR number (the index of the array).  If you pass no parameters, it 
    // will remove the element last returned by getRR.
    
function removeRR($specific = -1)
    {
        if (
$specific != -1) {
            if (
$specific == 0) {
                
$this->error "Cannot delete the SOA record";
                return(
0);
            } else {
                
$this->curRR $specific;
            }
        } else {
            
$this->curRR--;
        }
        for (
$ctr $this->curRR+1$ctr $this->rrCount(); $ctr++)
            
$this->rr[$ctr-1] = $this->rr[$ctr];
        unset(
$this->rr[$ctr-1]);
    }

    
// Create a new zone (in memory).  You must save the file to put it
    // on disk.
    
function addZone($zonename)
    {
        if (
$this->zoneconf->findZoneFile($zonename))
        {
            
$this->error "Could not add zone: zone already exists";
            return(
0);
        }

        if (! 
$this->zoneconf->addZone($zonename))
        {
            
$this->error $this->zoneconf->error;
            return(
0);
        }

        
$this->domainName $zonename;
        
$this->origin $zonename ".";
        
$this->defaultTTL 259200;

        
// Set the SOA record
        
$this->rr[0] = new RR;
        
$this->rr[0]->id 0;
        
$this->rr[0]->lhs $this->origin;
        
$this->rr[0]->ttl "";
        
$this->rr[0]->type "SOA";
        
$this->rr[0]->rhs = new SOA;
        
$this->rr[0]->rhs->priNS DEFAULT_PRIMARY_NAMESERVER;
        
$this->rr[0]->rhs->techContact DEFAULT_CONTACT_EMAIL;
        
$this->rr[0]->rhs->serial $this->rr[0]->rhs->setSerial();
        
$this->rr[0]->rhs->refresh 43200;
        
$this->rr[0]->rhs->retry 7200;
        
$this->rr[0]->rhs->expire 1209600;
        
$this->rr[0]->rhs->minimum 3600;

        
// Set the primary nameserver
        
$this->rr[1] = new RR;
        
$this->rr[1]->id 1;
        
$this->rr[1]->lhs $this->origin;
        
$this->rr[1]->ttl "";
        
$this->rr[1]->type "NS";
        
$this->rr[1]->rhs = new NS;
        
$this->rr[1]->rhs->hostname DEFAULT_PRIMARY_NAMESERVER;

        
// Set the secondary nameserver
        
$this->rr[2] = new RR;
        
$this->rr[2]->id 2;
        
$this->rr[2]->lhs $this->origin;
        
$this->rr[2]->ttl "";
        
$this->rr[2]->type "NS";
        
$this->rr[2]->rhs = new NS;
        
$this->rr[2]->rhs->hostname DEFAULT_SECONDARY_NAMESERVER;

        
// Set the primary mail exchanger
        
$this->rr[3] = new RR;
        
$this->rr[3]->id 3;
        
$this->rr[3]->lhs $this->origin;
        
$this->rr[3]->ttl "";
        
$this->rr[3]->type "MX";
        
$this->rr[3]->rhs = new MX;
        
$this->rr[3]->rhs->preference "10";
        
$this->rr[3]->rhs->hostname DEFAULT_PRIMARY_MX;

        
// Set the secondary mail exchanger
        
$this->rr[4] = new RR;
        
$this->rr[4]->id 4;
        
$this->rr[4]->lhs $this->origin;
        
$this->rr[4]->ttl "";
        
$this->rr[4]->type "MX";
        
$this->rr[4]->rhs = new MX;
        
$this->rr[4]->rhs->preference "20";
        
$this->rr[4]->rhs->hostname DEFAULT_SECONDARY_MX
        if (! 
$this->saveZone())
            return(
0);
        return(
1);
    }

    
// Remove a zone file
    
function removeZone($zonename)
    {
        if (! 
$this->zoneconf->findZoneFile($zonename))
        {
            
$this->error "Couldn't remove zone: zone does not exist";
            return(
0);
        }

        if (! 
$this->zoneconf->removeZone($zonename))
        {
            
$this->error $this->zoneconf->error;
            return(
0);
        }

        if (! 
unlink(NAMED_DIR "/" $this->zoneconf->file))
        {
            
$this->error "could not remove zone file";
            return(
0);
        }
        return(
1);
    }
}

// Object defines a resource record.  the $rhs property is an object
// of type SOA, NS, MX, A, CNAME, or PTR
class RR {
    var 
$id;
    var 
$lhs;
    var 
$ttl;
    var 
$type;
    var 
$rhs;
}

class 
SOA {
    var 
$priNS;
    var 
$techContact;
    var 
$serial "";
    var 
$refresh "";
    var 
$retry "";
    var 
$expire "";
    var 
$minimum "";

    function 
setSerial()
    {
        
$newSerial date("Ymd") . "01";
        if (
$this->serial >= $newSerial)
            return(
$this->serial++);
        else
            return(
$this->serial $newSerial);
    }
}

class 
NS {
    var 
$hostname;
}

class 
MX {
    var 
$preference;
    var 
$hostname;
}

class 
{
    var 
$ipAddress;
}

class 
PTR {
    var 
$hostname;
}

class 
CNAME {
    var 
$hostname;
}

class 
ZONECONF {
    var 
$domainName;
    var 
$file;
    var 
$error;

    
// Get's the filename of the specified zone.
    
function findZoneFile($domain)
    {
        if (! 
$ncStat stat(NAMED_CONF_PATH))
        {
            
$this->error "Couldn't stat NAMED_CONF_PATH";
            return(
0);
        }

        if (! (
$ncpStat stat(NAMED_CONF_PATH_PARSED)) || $ncStat[9] > $ncpStat[9])
        {
            
$this->readNamedConf();
        }

        if (! (
$f fopen(NAMED_DIR "/named.conf.parsed""r")))
        {
            
$this->error "Couldn't open " NAMED_DIR "/named.conf.parsed for read";
            return(
0);
        }

        while (! 
feof($f))
        {
            
$line fgets($f1024);
            
$d explode(":"$line);
            if (
$d[0] == $domain)
            {
                
$this->domainName $d[0];
                
$this->file chop($d[1]);
                
fclose($f);
                return(
1);
            }
        }
        
fclose($f);
        
$this->error "zone does not exist";
        return(
0);
    }

    
// returns a list of domains with searchSpec as a substring of the zone name
    
function findZone($searchSpec)
    {
        if (! 
$ncStat stat(NAMED_CONF_PATH))
        {
            
$this->error "Couldn't stat NAMED_CONF_PATH";
            return(
0);
        }

        if (! (
$ncpStat stat(NAMED_CONF_PATH_PARSED)) || $ncStat[9] > $ncpStat[9])
            
$this->readNamedConf();

        if (! (
$f fopen(NAMED_DIR "/named.conf.parsed""r")))
        {
            
$this->error "Couldn't open " NAMED_DIR "/named.conf.parsed for read";
            return(
0);
        }

        
$ctr 0;
        if (
$searchSpec != "*")
        {
            while (! 
feof($f))
            {
                
$line fgets($f1024);
                
$d explode(":"$line);
                if (
$d[0] == $searchSpec)
                    
$dlist[$ctr++] = $d[0];
            }
            if (
$dlist[0] != "")
                
$flag 1;
            
rewind($f);
            while (! 
feof($f))
            {
                
$line fgets($f1024);
                
$d explode(":"$line);
                if (
strstr($d[0], $searchSpec))
                    
$dlistu[$ctr++] = $d[0];
            }
            if (
count($dlistu))
                
sort($dlistu);
            if (
$flag == 1)
            {
                for (
$ctr 0$ctr count($dlistu); $ctr++)
                    
$dlist[$ctr+1] = $dlistu[$ctr];
                return(
$dlist);
            } else
                return(
$dlistu);
        } else {
            while (! 
feof($f))
            {
                
$line fgets($f1024);
                
$d explode(":"$line);
                
$dlist[$ctr++] = $d[0];
            }
            
sort($dlist);
            return(
$dlist);
        }
    }

    
// Read and parse the named.conf file
    
function readNamedConf($includedfile ""$fp 0)
    {
        if (
$includedfile)
        {
            if (! (
$f fopen(NAMED_DIR "/" $includedfile"r")))
            {
                
$this->error "Couldn't open " NAMED_DIR "/" $includedfile " for read";
                return(
0);
            }
        } else {
            if (! (
$f fopen(NAMED_CONF_PATH"r")))
            {
                
$this->error "Couldn't open " NAMED_CONF_PATH " for read";
                return(
0);
            }
        }

        if (! 
$fp && !($fp fopen(NAMED_CONF_PATH_PARSED"w")))
        {
            
fclose($f);
            
$this->error "Couldn't open 'NAMED_CONF_PATH_PARSED' for write";
            return(
0);
        }

        while (! 
feof($f))
        {
            
$line fgets($f1024);
            
$linenum++;
            if (
ereg("^[ \t]*//.*$"$line$regs))
                continue;

            
// Look for an include keyword
            
if (ereg("^[ \t]*include[ \t]*\"([^\"]+)\""$line$regs)) {
                
$this->readNamedConf($regs[1], $fp);
            }

            
// Looking for the zone keyword
            
if (ereg("^[ \t]*zone[ \t]*\"([^\"]+)\"[ \t]*(.*)$"$line$regs)) {
                
$zonedef $regs[2];
                
$zchar "";
                
$ref 1;
                while (
$ref != && ! feof($f))
                {
                    
$zchar fgetc($f);
                    
$zonedef .= $zchar;
                    if (
$zchar == "{")
                        
$ref++;
                    else if (
$zchar == "}")
                        
$ref--;
                }
                
$type ereg_replace(".*type[ \t]+([^;]+);.*""\\1"$zonedef);
                if (
strcasecmp($type"master") == 0)
                {
                    
$file ereg_replace(".*file[ \t]+\"(.*)\";.*""\\1"$zonedef);
                    
fputs($fp$regs[1] . ":" $file "\n");
                }
            }
        }
        if (! 
$includedfile)
            
fclose($fp);
        
fclose($f);
        return(
1);
    }

    
// Add a new zone to the named.conf file.
    
function addZone($zonename)
    {
        while (
file_exists(NAMED_CONF_MASTER_ADD_FILE ".lock") && $ctr++ < 10)
            
usleep(10);

        if (
file_exists(NAMED_CONF_MASTER_ADD_FILE ".lock"))
        {
            
$this->error "Couldn't add zone: " NAMED_CONF_MASTER_ADD_FILE " is locked.";
            return(
0);
        }

        if (! 
touch(NAMED_CONF_MASTER_ADD_FILE ".lock"))
        {
            
$this->error "Couldn't add zone: can't touch " NAMED_CONF_MASTER_ADD_FILE ".lock";
            return(
0);
        }

        if (
$this->findZoneFile($zonename))
        {
            
$this->error "Couldn't add zone: zone already exists";
            
unlink(NAMED_CONF_MASTER_ADD_FILE ".lock");
            return(
0);
        }

        if (! (
$f fopen(NAMED_CONF_MASTER_ADD_FILE"a")))
        {
            
$this->error "Couldn't add zone: Couldn't open " NAMED_CONF_MASTER_ADD_FILE " for append";
            
unlink(NAMED_CONF_MASTER_ADD_FILE ".lock");
            return(
0);
        }

        if (! (
$f2 fopen(NAMED_CONF_PATH_PARSED"a")))
        {
            
$this->error "Couldn't add zone: Couldn't open " NAMED_CONF_PATH_PARSED " for append";
            
unlink(NAMED_CONF_MASTER_ADD_FILE ".lock");
            return(
0);
        }

        
$adds =
'zone "' $zonename '" {
    type master;
    file "masters/' 
$zonename[0] . '/' $zonename '";
};

'
;
        
fputs($f$adds);
        
fclose($f);
        
$this->domainName $zonename;
        
$this->file 'masters/' $zonename[0] . '/' $zonename;
        
fputs($f2$zonename ":" $this->file "\n");
        
fclose($f2);
        
touch(NAMED_CONF_PATH_PARSEDtime()+1);
        
unlink(NAMED_CONF_MASTER_ADD_FILE ".lock");
        return(
1);
    }

    
// remove a zone from the named.conf file
    
function removeZone($zonename)
    {
        while (
file_exists(NAMED_CONF_MASTER_ADD_FILE ".lock") && $ctr++ < 10)
            
usleep(10);

        if (
file_exists(NAMED_CONF_MASTER_ADD_FILE ".lock"))
        {
            
$this->error "Couldn't remove zone: " NAMED_CONF_MASTER_ADD_FILE " is locked.";
            return(
0);
        }

        if (! 
touch(NAMED_CONF_MASTER_ADD_FILE ".lock"))
        {
            
$this->error "Couldn't remove zone: can't touch " NAMED_CONF_MASTER_ADD_FILE ".lock";
            return(
0);
        }

        if (! 
$this->findZoneFile($zonename))
        {
            
$this->error "Couldn't remove zone: zone does not exist";
            
unlink(NAMED_CONF_MASTER_ADD_FILE ".lock");
            return(
0);
        }

        if (! (
$fr fopen(NAMED_CONF_MASTER_ADD_FILE"r")))
        {
            
$this->error "Couldn't remove zone: Couldn't open " NAMED_CONF_MASTER_ADD_FILE " for read";
            
unlink(NAMED_CONF_MASTER_ADD_FILE ".lock");
            return(
0);
        }

        if (! (
$fw fopen(NAMED_CONF_MASTER_ADD_FILE ".new""w")))
        {
            
$this->error "Couldn't remove zone: Couldn't open " NAMED_CONF_MASTER_ADD_FILE ".new for write";
            
unlink(NAMED_CONF_MASTER_ADD_FILE ".lock");
            return(
0);
        }

        if (! (
$f2r fopen(NAMED_CONF_PATH_PARSED"r")))
        {
            
$this->error "Couldn't remove zone: Couldn't open " NAMED_CONF_PATH_PARSED " for read";
            
unlink(NAMED_CONF_MASTER_ADD_FILE ".lock");
            return(
0);
        }

        if (! (
$f2w fopen(NAMED_CONF_PATH_PARSED ".new""w")))
        {
            
$this->error "Couldn't remove zone: Couldn't open " NAMED_CONF_PATH_PARSED ".new for write";
            
unlink(NAMED_CONF_MASTER_ADD_FILE ".lock");
            return(
0);
        }

        while (! 
feof($fr))
        {
            
$line fgets($fr1024);

            if (
ereg("^[ \t]*zone[ \t]*\"$zonename\"[ \t]*(.*)$"$line$regs)) {
                
$zonedef $regs[2];
                
$zchar "";
                
$ref 1;
                while (
$ref != && ! feof($fr))
                {
                    
$zchar fgetc($fr);
                    
$zonedef .= $zchar;
                    if (
$zchar == "{")
                        
$ref++;
                    else if (
$zchar == "}")
                        
$ref--;
                }
                while (
$zchar != ";" && ! feof($fr))
                    
$zchar fgetc($fr);
                while (
$zchar != "\n" && ! feof($fr))
                    
$zchar fgetc($fr);
                
$line "";
            }
            
fputs($fw$line);
        }
        
fclose($fr);
        
fclose($fw);
        
rename(NAMED_CONF_MASTER_ADD_FILE ".new"NAMED_CONF_MASTER_ADD_FILE);

        while (! 
feof($f2r))
        {
            
$line fgets($f2r1024);
            
$d explode(":"$line);
            if (
$d[0] != $zonename)
                
fputs($f2w$line);
        }
        
rename(NAMED_CONF_PATH_PARSED ".new"NAMED_CONF_PATH_PARSED);
        
touch(NAMED_CONF_PATH_PARSEDtime()+1);
        
unlink(NAMED_CONF_MASTER_ADD_FILE ".lock");
        return(
1);
    }
}
?>