Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not Just NS records for "TLDs" #175

Open
wvro-org opened this issue Sep 21, 2024 · 5 comments
Open

Not Just NS records for "TLDs" #175

wvro-org opened this issue Sep 21, 2024 · 5 comments

Comments

@wvro-org
Copy link
Contributor

wvro-org commented Sep 21, 2024

Add ability to specify other recods for "tlds"
for example now it's not possible to specify for example
A record for "tld" .test
A record for "tld" .com.test
and manually to as write-zone.php then "deletes" other records....

Smhw
so smth like this setting in config.php

// Custom Records Configuration
'tld_custom_records' => [
    '.test' => [
        'enabled' => true,
        'records' => [
            'A' => '0.0.0.0',
            'TXT' => 'test',
            'MX' => 'mx.example.com'
        ]
    ],
    '.com.test' => [
        'enabled' => true,
        'records' => [
            'A' => '0.0.0.0',
            'TXT' => 'test',
            'MX' => 'mx.example.com'
        ]
    ],
],

and write-zone.php should support these
Constants for supported record types

const RECORD_TYPES = [
'A' => 'A',
'AAAA' => 'AAAA',
'MX' => 'MX',
'NS' => 'NS',
'PTR' => 'PTR',
'SOA' => 'SOA',
'TXT' => 'TXT',
'SRV' => 'SRV',
'CNAME' => 'CNAME',
'CAA' => 'CAA',
'DNSKEY' => 'DNSKEY',
'DS' => 'DS',
'NSEC' => 'NSEC',
'NSEC3' => 'NSEC3',
'RRSIG' => 'RRSIG',
'SPF' => 'SPF',
'SSHFP' => 'SSHFP',
'TLSA' => 'TLSA',
'URI' => 'URI',
'LOC' => 'LOC',
];

// Constants for supported record types

@wvro-org
Copy link
Contributor Author

wvro-org commented Sep 22, 2024

added bellow // DS records for the domain
logic for custom
smth like this logic not working

if (isset($c['tld_custom_records'][$dname]) && $c['tld_custom_records'][$dname]['enabled']) { $customRecords = $c['tld_custom_records'][$dname]['records']; foreach ($customRecords as $recordType => $values) { foreach ($values as $value) { $record = new ResourceRecord; $record->setName($dname_clean . '.'); $record->setClass(Classes::INTERNET); $record->setRdata(Factory::$recordType($value)); $zone->addResourceRecord($record); } } }

@wvro-org
Copy link
Contributor Author

wvro-org commented Sep 22, 2024

@getpinga
@anantsparrow
need help stucked here (even with phind)


We've created a new CustomRecord class that extends ResourceRecord.
The custom record processing logic has been moved to a separate section.
Regular records (NS, A/AAAA, DS) are processed in the main loop.
The CustomRecord class is used for custom records, while regular records use the standard ResourceRecord class.

class CustomRecord extends ResourceRecord
{
public function __construct()
{
parent::__construct();
}
}

//custom tld record
// Check if custom records are enabled for this TLD
if (isset($c['tld_custom_records'][$tld]) && $c['tld_custom_records'][$tld]['enabled']) {
$customRecords = $c['tld_custom_records'][$tld]['records'];

// Process A records
if (isset($customRecords['A'])) {
    foreach ($customRecords['A'] as $value) {
        $customRecord = new CustomRecord();
        $customRecord->setName($dname_clean . '.');
        $customRecord->setClass(Classes::INTERNET);
        $customRecord->setRdata(Factory::A($value));
        $zone->addResourceRecord($customRecord);
    }
}

// Process AAAA records
if (isset($customRecords['AAAA'])) {
    foreach ($customRecords['AAAA'] as $value) {
        $customRecord = new CustomRecord();
        $customRecord->setName($dname_clean . '.');
        $customRecord->setClass(Classes::INTERNET);
        $customRecord->setRdata(Factory::AAAA($value));
        $zone->addResourceRecord($customRecord);
    }
}

// Process TXT records
if (isset($customRecords['TXT'])) {
    foreach ($customRecords['TXT'] as $value) {
        $customRecord = new CustomRecord();
        $customRecord->setName($dname_clean . '.');
        $customRecord->setClass(Classes::INTERNET);
        $customRecord->setRdata(Factory::Txt($value));
        $zone->addResourceRecord($customRecord);
    }
}

// Process MX records
if (isset($customRecords['MX'])) {
    foreach ($customRecords['MX'] as $value) {
        $record = new ResourceRecord();
        $record->setName($dname_clean . '.');
        $record->setClass(Classes::INTERNET);
        $record->setRdata(Factory::Mx($value));
        $zone->addResourceRecord($customrecord);
    }
}

// Log custom record processing
$log->info("Processed custom records for TLD {$tld}:");
$log->info("A records: " . count($customRecords['A'] ?? []));
$log->info("TXT records: " . count($customRecords['TXT'] ?? []));
$log->info("MX records: " . count($customRecords['MX'] ?? []));

} else {
$log->info("No custom records found for TLD {$tld}");
}

@wvro-org
Copy link
Contributor Author

wvro-org commented Sep 23, 2024

figured out .... will upload later in next comment

@wvro-org
Copy link
Contributor Author

here's long awaited IPV4/6 write-zone-ipv4-ipv6.php (thanks phind ai )

<?php

require_once 'vendor/autoload.php';

use Badcow\DNS\Zone;
use Badcow\DNS\Rdata\Factory;
use Badcow\DNS\ResourceRecord;
use Badcow\DNS\Classes;
use Badcow\DNS\ZoneBuilder;
use Badcow\DNS\AlignedBuilder;

$c = require_once 'config.php';
require_once 'helpers.php';

$logFilePath = '/var/log/namingo/write_zone.log';
$log = setupLogger($logFilePath, 'Zone_Generator');
$log->info('job started.');

use Swoole\Coroutine;

// Initialize the PDO connection pool
$pool = new Swoole\Database\PDOPool(
    (new Swoole\Database\PDOConfig())
        ->withDriver($c['db_type'])
        ->withHost($c['db_host'])
        ->withPort($c['db_port'])
        ->withDbName($c['db_database'])
        ->withUsername($c['db_username'])
        ->withPassword($c['db_password'])
        ->withCharset('utf8mb4')
);

Swoole\Runtime::enableCoroutine();


 // A / A6 record for zone
function addSpecificRecords($zone, $cleanedTld, $ipv4Address, $ipv6Address) {
    global $log;

    // Log that the function is being called
    $log->info('Zone_Generator.INFO: Adding specific records for domain: ' . $cleanedTld);

    try {
        // Add A record
        $record = generateARecord($cleanedTld, $ipv4Address);
        if ($record !== null) {
            $zone->addResourceRecord($record);
            $log->info('Zone_Generator.INFO: Added A record for ' . $cleanedTld . ': ' . $ipv4Address);
        } else {
            $log->warning('Zone_Generator.WARNING: Failed to add A record for domain: ' . $cleanedTld);
        }

        // Add AAAA record
        $record = generateAAAARecord($cleanedTld, $ipv6Address);
        if ($record !== null) {
            $zone->addResourceRecord($record);
            $log->info('Zone_Generator.INFO: Added AAAA record for ' . $cleanedTld . ': ' . $ipv6Address);
        } else {
            $log->warning('Zone_Generator.WARNING: Failed to add AAAA record for domain: ' . $cleanedTld);
        }
    } catch (Exception $e) {
        $log->error('Zone_Generator.ERROR: Failed to add specific records for domain: ' . $cleanedTld . '. Error: ' . $e->getMessage());
    }
}

function generateARecord($hostName, $ipAddress) {
    $record = new ResourceRecord();
    $record->setName($hostName . '.');
    $record->setClass(Classes::INTERNET);
    $record->setTtl(100);
    $record->setRdata(Factory::A($ipAddress));
    return $record;
}

function generateAAAARecord($hostName, $ipv6Address) {
    $record = new ResourceRecord();
    $record->setName($hostName . '.');
    $record->setClass(Classes::INTERNET);
    $record->setTtl(100);
    $record->setRdata(Factory::AAAA($ipv6Address));
    return $record;
}

 // A / A6 record for zone end



// Creating first coroutine
Coroutine::create(function () use ($pool, $log, $c) {
    try {
        $pdo = $pool->get();
        $sth = $pdo->prepare('SELECT id, tld FROM domain_tld');
        $sth->execute();
        $timestamp = time();

        while (list($id, $tld) = $sth->fetch(PDO::FETCH_NUM)) {
            $tldRE = preg_quote($tld, '/');
            $cleanedTld = ltrim(strtolower($tld), '.');
            $zone = new Zone($cleanedTld.'.');
            $zone->setDefaultTtl(100);

            //setting for  // A / A6 record for zones
            if ($cleanedTld === 'test') {
                addSpecificRecords($zone, $cleanedTld, '0.0.0.0', '::1'); leave blank to delete A, and AAAA records
            } elseif ($cleanedTld === 'com.test') {
                addSpecificRecords($zone, $cleanedTld, '', ''); 
            }  else {
                // Do nothing for other domains
            }
             //setting for  // A / A6 record for zones end
 
            
            $soa = new ResourceRecord;
            $soa->setName('@');
            $soa->setClass(Classes::INTERNET);
            $soa->setRdata(Factory::Soa(
                $c['ns']['ns1'] . '.',
                $c['dns_soa'] . '.',
                $timestamp, 
                900, 
                1800, 
                3600000, 
                3600
            ));
            $zone->addResourceRecord($soa);
            // Add A and AAAA records
           
            foreach ($c['ns'] as $ns) {
                $nsRecord = new ResourceRecord;
                $nsRecord->setName($cleanedTld . '.');
                $nsRecord->setClass(Classes::INTERNET);
                $nsRecord->setRdata(Factory::Ns($ns . '.'));
                $zone->addResourceRecord($nsRecord);
            }


            
            // Fetch domains for this TLD
            $sthDomains = $pdo->prepare('SELECT DISTINCT domain.id, domain.name FROM domain WHERE tldid = :id AND (exdate > CURRENT_TIMESTAMP OR rgpstatus = \'pendingRestore\') ORDER BY domain.name');
            
            $domainIds = [];
            $sthDomains->execute([':id' => $id]);
            while ($row = $sthDomains->fetch(PDO::FETCH_ASSOC)) {
                $domainIds[] = $row['id'];
            }

            $statuses = [];
            if (count($domainIds) > 0) {
                $placeholders = implode(',', array_fill(0, count($domainIds), '?'));
                $sthStatus = $pdo->prepare("SELECT domain_id, id FROM domain_status WHERE domain_id IN ($placeholders) AND status LIKE '%Hold'");
                $sthStatus->execute($domainIds);
                while ($row = $sthStatus->fetch(PDO::FETCH_ASSOC)) {
                    $statuses[$row['domain_id']] = $row['id'];
                }
            }
            
            $sthDomains->execute([':id' => $id]);

            while (list($did, $dname) = $sthDomains->fetch(PDO::FETCH_NUM)) {
                if (isset($statuses[$did])) continue;

                $dname_clean = $dname;
                $dname_clean = ($dname_clean == "$tld.") ? '@' : $dname_clean;

                // NS records for the domain
                $sthNsRecords = $pdo->prepare('SELECT DISTINCT host.name FROM domain_host_map INNER JOIN host ON domain_host_map.host_id = host.id WHERE domain_host_map.domain_id = :did');
                $sthNsRecords->execute([':did' => $did]);
                while (list($hname) = $sthNsRecords->fetch(PDO::FETCH_NUM)) {
                    $nsRecord = new ResourceRecord;
                    $nsRecord->setName($dname_clean . '.');
                    $nsRecord->setClass(Classes::INTERNET);
                    $nsRecord->setRdata(Factory::Ns($hname . '.'));
                    $zone->addResourceRecord($nsRecord);
                }


                // A/AAAA records for the domain
                $sthHostRecords = $pdo->prepare("SELECT host.name, host_addr.ip, host_addr.addr FROM host INNER JOIN host_addr ON host.id = host_addr.host_id WHERE host.domain_id = :did ORDER BY host.name");
                $sthHostRecords->execute([':did' => $did]);
                while (list($hname, $type, $addr) = $sthHostRecords->fetch(PDO::FETCH_NUM)) {
                    $hname_clean = $hname;
                    $hname_clean = ($hname_clean == "$tld.") ? '@' : $hname_clean;
                    $record = new ResourceRecord;
                    $record->setName($hname_clean . '.');
                    $record->setClass(Classes::INTERNET);

                    if ($type == 'v4') {
                        $record->setRdata(Factory::A($addr));
                    } else {
                        $record->setRdata(Factory::AAAA($addr));
                    }

                    $zone->addResourceRecord($record);
                }

                // DS records for the domain
                $sthDS = $pdo->prepare("SELECT keytag, alg, digesttype, digest FROM secdns WHERE domain_id = :did");
                $sthDS->execute([':did' => $did]);
                while (list($keytag, $alg, $digesttype, $digest) = $sthDS->fetch(PDO::FETCH_NUM)) {
                    $dsRecord = new ResourceRecord;
                    $dsRecord->setName($dname_clean . '.');
                    $dsRecord->setClass(Classes::INTERNET);
                    $dsRecord->setRdata(Factory::Ds($keytag, $alg, hex2bin($digest), $digesttype));
                    $zone->addResourceRecord($dsRecord);
                }
            }


            
            
           

            if (isset($c['zone_mode']) && $c['zone_mode'] === 'nice') {
                $builder = new AlignedBuilder();
            } else {
                $builder = new ZoneBuilder();
            }
            
            $log->info('Building zone for TLD: ' . $cleanedTld . ', Builder type: ' . ($c['zone_mode'] === 'nice' ? 'AlignedBuilder' : 'ZoneBuilder'));
            $completed_zone = $builder->build($zone);

      



            // Log a truncated version of the completed zone content
$maxLogLength = 10000; // Maximum length for the log entry
$logContent = substr($completed_zone, 0, $maxLogLength);
$log->info("Completed zone content (truncated): " . $logContent);






            if ($c['dns_server'] == 'bind') {
                $basePath = '/var/lib/bind';
            } elseif ($c['dns_server'] == 'nsd') {
                $basePath = '/etc/nsd';
            } elseif ($c['dns_server'] == 'knot') {
                $basePath = '/etc/knot';
            } else {
                // Default path
                $basePath = '/var/lib/bind';
            }

            file_put_contents("{$basePath}/{$cleanedTld}.zone", $completed_zone);

            if ($c['dns_server'] == 'opendnssec') {
                chown("{$basePath}/{$cleanedTld}.zone", 'opendnssec');
                chgrp("{$basePath}/{$cleanedTld}.zone", 'opendnssec');
            }

        }

        if ($c['dns_server'] == 'bind') {
            exec("rndc reload {$cleanedTld}.", $output, $return_var);
            if ($return_var != 0) {
                $log->error('Failed to reload BIND. ' . $return_var);
            }

            exec("rndc notify {$cleanedTld}.", $output, $return_var);
            if ($return_var != 0) {
                $log->error('Failed to notify secondary servers. ' . $return_var);
            }
        } elseif ($c['dns_server'] == 'nsd') {
            exec("nsd-control reload", $output, $return_var);
            if ($return_var != 0) {
                $log->error('Failed to reload NSD. ' . $return_var);
            }
        } elseif ($c['dns_server'] == 'knot') {
            exec("knotc reload", $output, $return_var);
            if ($return_var != 0) {
                $log->error('Failed to reload Knot DNS. ' . $return_var);
            }

            exec("knotc zone-notify {$cleanedTld}.", $output, $return_var);
            if ($return_var != 0) {
                $log->error('Failed to notify secondary servers. ' . $return_var);
            }
        } elseif ($c['dns_server'] == 'opendnssec') {
            exec("ods-signer sign {$cleanedTld}");
            sleep(1);
            copy("/var/lib/opendnssec/signed/{$cleanedTld}", "/var/lib/bind/{$cleanedTld}.zone.signed");

            exec("rndc reload {$cleanedTld}.", $output, $return_var);
            if ($return_var != 0) {
                $log->error('Failed to reload BIND. ' . $return_var);
            }

            exec("rndc notify {$cleanedTld}.", $output, $return_var);
            if ($return_var != 0) {
                $log->error('Failed to notify secondary servers. ' . $return_var);
            }
        } else {
            // Default
            exec("rndc reload {$cleanedTld}.", $output, $return_var);
            if ($return_var != 0) {
                $log->error('Failed to reload BIND. ' . $return_var);
            }

            exec("rndc notify {$cleanedTld}.", $output, $return_var);
            if ($return_var != 0) {
                $log->error('Failed to notify secondary servers. ' . $return_var);
            }
        }
    
        $log->info('job finished successfully.');
    } catch (PDOException $e) {
        $log->error('Database error: ' . $e->getMessage());
    } catch (Throwable $e) {
        $log->error('Error: ' . $e->getMessage());
    } finally {
        // Return the connection to the pool
        $pool->put($pdo);
    }
});

@wvro-org
Copy link
Contributor Author

wvro-org commented Sep 24, 2024

not closing working on other supported records...

Type Type ID Defining RFC Supported? Usage*
A 1 RFC 1035 In Use
AAAA 28 RFC 3596 In Use
AFSDB 18 RFC 1183 In Use
APL 42 RFC 3123 In Use
CAA 257 RFC 6844 In Use
CDNSKEY 60 RFC 7344 In Use
CDS 59 RFC 7344 In Use
CERT 37 RFC 4398 In Use
CNAME 5 RFC 1035 In Use
CSYNC 62 RFC 7477 In Use
DHCID 49 RFC 4701 In Use
DLV 32769 RFC 4431 In Use
DNAME 39 RFC 6672 In Use
DNSKEY 48 RFC 4034 In Use
DS 43 RFC 4034 In Use
HIP 55 RFC 8005 In Use
IPSECKEY 45 RFC 4025 In Use
KEY 25 RFC 2535 & RFC 2930 In Use
KX 36 RFC 2230 In Use
LOC 29 RFC 1876 In Use
MX 15 RFC 1035 & RFC 7505 In Use
NAPTR 35 RFC 3403 In Use
NSEC 47 RFC 4034 In Use
NSEC3 50 RFC 5155 In Use
NSEC3PARAM 51 RFC 5155 In Use
OPENPGPKEY 61 RFC 7929 In Use
PTR 12 RFC 1035 In Use
RP 17 RFC 1183 In Use
RRSIG 46 RFC 4034 In Use
SIG 24 RFC 2535 In Use
SMIMEA 53 RFC 8162 In Use
SRV 33 RFC 2782 In Use
SSHFP 44 RFC 4255 In Use
TA 32768 N/A In Use
TKEY 249 RFC 2930 In Use
TLSA 52 RFC 6698 In Use
TSIG 250 RFC 2845 In Use
TXT 16 RFC 1035 In Use
URI 256 RFC 7553 In Use

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant