getnamingo / registry

Open-source domain registry platform. Revolutionizing ccTLD and gTLD management with Namingo.
https://namingo.org
MIT License
34 stars 6 forks source link

Not Just NS records for "TLDs" #175

Open wvro-org opened 1 week ago

wvro-org commented 1 week ago

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 commented 1 week ago

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 commented 1 week ago

@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 commented 6 days ago

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

wvro-org commented 6 days ago

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 commented 5 days ago
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