<?php

/* This is script for configuration of BIND DNS server.
 * Use "sudo aptitude install bind9" to install server under Ubuntu
 */

class ConfigDNS extends NetworkConfigItem
{
  function GenerateDNS(array $DNS): void
  {
    $Output = '$ORIGIN '.$DNS['Domain'].'.'."\n".
        '$TTL '.$DNS['TTL']."\n".
        $DNS['Domain'].".\tIN\tSOA\t".$DNS['NameServer'][0].".\troot.".$DNS['Domain'].".  (\n".
        "\t\t\t".$DNS['Serial']."\t; serial\n".
        "\t\t\t".$DNS['Refresh']."\t; refresh\n".
        "\t\t\t".$DNS['Retry']."\t; retry\n".
        "\t\t\t".$DNS['Expire']."\t; expire\n".
        "\t\t\t".$DNS['Minimum']."\t; minimum\n".
        "\t\t\t)\n";
    foreach ($DNS['NameServer'] as $NameServer)
    {
      $Output .= "\t\tIN\tNS\t".strtolower($NameServer).".\n";
    }
    $Output .= "\t\t\tTXT\t".'"'.$DNS['Description'].'"'."\n";

    // Mail server records
    $Priority = 10;
    foreach ($DNS['MailServer'] as $MailServer)
    {
      $Output .="\t\t\tMX\t".$Priority." ".strtolower($MailServer).".\n";
      $Priority += 10;
    }
    $Output .= "; SPF\n".
        $DNS['Domain'].".\tIN\tTXT\t\"v=spf1 mx -all\"\n".
        $DNS['Domain'].".\tIN\tSPF\t\"v=spf1 mx -all\"\n";
    foreach ($DNS['MailServer'] as $MailServer)
    {
      $Output .= $MailServer.".\tIN\tTXT\t\"v=spf1 a -all\"\n".
          $MailServer.".\tIN\tSPF\t\"v=spf1 a -all\"\n";
    }

    // Base server
    $Output .= "@\tIN\tA\t".strtolower($DNS['BaseServer'])."\n";
    $Output .= "@\tIN\tAAAA\t".strtolower($DNS['BaseServerIPv6'])."\n";

    // IPv4 host list
    foreach ($DNS['Host'] as $Host)
    {
      if (strlen($Host['Name']) < 8) $Host['Name'] .= "\t";
      $Output .= strtolower($Host['Name'])."\tIN\tA\t".$Host['Address']."\n";
    }

    // IPv6 host list
    foreach ($DNS['Host'] as $Host)
    {
      if (strlen($Host['Name']) < 8) $Host['Name'] .= "\t";
      if (array_key_exists('IPv6', $Host) and ($Host['IPv6'] != ''))
        $Output .= strtolower($Host['Name'])."\tIN\tAAAA\t".$Host['IPv6']."\n";
    }

    // Alias list
    foreach ($DNS['Alias'] as $Alias)
    {
      if (strlen($Alias['Name']) < 8) $Alias['Name'] .= "\t";
      $Output .= strtolower($Alias['Name'])."\tIN\tCNAME\t".strtolower($Alias['Target'])."\n";
    }

    $File = fopen($DNS['BaseDir'].$DNS['Domain'].'.zone', 'w');
    fputs($File, $Output);
    fclose($File);

    // Generate reverse DNS records
    foreach ($DNS['Network'] as $Network)
    {
      $Parts = explode('.', $Network);
      $Output = // '$ORIGIN '.$Parts[2].'.'.$Parts[1].'.'.$Parts[0].'.in-addr.arpa'."\n".
      '$TTL '.$DNS['TTL']."\n".
      "@\tIN\tSOA\t".$DNS['ReverseNameServer'][0].".\troot.".$DNS['Domain'].".  (\n".
      "\t\t\t\t".$DNS['Serial']."\t; serial\n".
      "\t\t\t\t".$DNS['Refresh']."\t; refresh\n".
      "\t\t\t\t".$DNS['Retry']."\t; retry\n".
      "\t\t\t\t".$DNS['Expire']."\t; expire\n".
      "\t\t\t\t".$DNS['Minimum']."\t; minimum\n".
      "\t\t\t\t)\n";
      foreach ($DNS['ReverseNameServer'] as $NameServer)
      {
        if (substr($NameServer, -strlen($DNS['Domain'])) == $DNS['Domain'])
          $Output .= "@\tIN\tNS\t".$NameServer.".\n";
        else $Output .= "\tIN\tNS\t".$NameServer.".\n";
      }
      foreach ($DNS['Host'] as $Host)
        if (substr($Host['Address'], 0, strlen($Network)) == $Network)
        {
          $AddressParts = explode('.', $Host['Address']);
          $Host['Name'] = strtolower($Host['Name']);
          $Output .= $AddressParts[3]."\tIN\tPTR\t".$Host['Name'].".".$DNS['Domain'].".\n";
        }
      $File = fopen($DNS['BaseDir'].$Parts[2].'.'.$Parts[1].'.'.$Parts[0].'.in-addr.arpa.zone', 'w+');
      fputs($File, $Output);
      fclose($File);
    }

    // Generate reverse DNS IPv6 records
    foreach ($DNS['IPv6Network'] as $Network)
    {
      $Parts = explode('/', $Network);
      $NetworkAddress = $Parts[0];
      $Prefix = $Parts[1];
      $Output = // '$ORIGIN '.$Parts[2].'.'.$Parts[1].'.'.$Parts[0].'.in-addr.arpa'."\n".
      '$TTL '.$DNS['TTL']."\n".
      "@\tIN\tSOA\t".$DNS['ReverseNameServer'][0].".\troot.".$DNS['Domain'].".  (\n".
      "\t\t\t\t".$DNS['Serial']."\t; serial\n".
      "\t\t\t\t".$DNS['Refresh']."\t; refresh\n".
      "\t\t\t\t".$DNS['Retry']."\t; retry\n".
      "\t\t\t\t".$DNS['Expire']."\t; expire\n".
      "\t\t\t\t".$DNS['Minimum']."\t; minimum\n".
      "\t\t\t\t)\n";
      foreach ($DNS['ReverseNameServer'] as $NameServer)
      {
        if (substr($NameServer, -strlen($DNS['Domain'])) == $DNS['Domain'])
          $Output .= "@\tIN\tNS\t".$NameServer.".\n";
        else $Output .= "\tIN\tNS\t".$NameServer.".\n";
      }
      foreach ($DNS['Host'] as $Host)
        if (array_key_exists('IPv6', $Host) and ($Host['IPv6'] != ''))
        {
          $Addr = new NetworkAddressIPv6();
          $Addr->AddressFromString($Host['IPv6']);
          $Octets = $Addr->GetOctets();
          $Octets = array_slice($Octets, 0, (128 - $Prefix) / 4);
          $Octets = implode('.', $Octets);

          $Host['Name'] = strtolower($Host['Name']);
          $Output .= $Octets."\tIN\tPTR\t".$Host['Name'].".".$DNS['Domain'].".\n";
        }

      $NetAddr = new NetworkAddressIPv6();
      $NetAddr->AddressFromString($NetworkAddress);
      $Octets = array_reverse($NetAddr->GetOctets());
      $Octets = array_reverse(array_slice($Octets, 0, $Prefix / 4));
      $FileName = implode('.', $Octets).'.ip6.arpa.zone';
      $File = fopen($DNS['BaseDir'].$FileName, 'w+');
      fputs($File, $Output);
      fclose($File);
    }
  }

  function Run(): void
  {
    $BaseDomain = 'zdechov.net';
    $Now = getdate();
    $I = floor(($Now['hours'] * 60 * 60 + $Now['minutes'] * 60 + $Now['seconds']) / (24 * 60 * 60) * 100);
    $Serial = date('Ymd', time()).sprintf('%02d', $I);
    $MinimumTime = 7200;
    $RetryTime = 7200;
    $ExpireTime = 2419200;
    $RefreshTime = 28800;
    $MinimumTime = 10800;
    $TTL = 86400;
    if (isset($this->System->Config['DNS']['BaseDir']))
      $BaseDir = $this->System->Config['DNS']['BaseDir'];
      else $BaseDir = '/var/cache/bind';
    if (!file_exists($BaseDir)) die('Base directory "'.$BaseDir.'" not exists.');
    $MailServer = 'centrala';


    $BaseDomain = 'zdechov.net';
    $LocalDNS = array(
        'Domain' => $BaseDomain,
        'Serial' => $Serial,
        'Refresh' => $RefreshTime,
        'Expire' => $ExpireTime,
        'Retry' => $RetryTime,
        'Minimum' => $MinimumTime,
        'TTL' => $TTL,
        'Description' => 'ZdechovNET community network',
        'BaseDir' => $BaseDir.'/internal/',
        'MailServer' => array('mail.'.$BaseDomain),
        'NameServer' => array('centrala.'.$BaseDomain, 'mozek.'.$BaseDomain),
        'BaseServer' => '10.145.64.23', // warp
        'BaseServerIPv6' => '2a00:e580:244:1::7', // warp
        'ReverseNameServer' => array('centrala.'.$BaseDomain, 'mozek.'.$BaseDomain),
        'Host' => array(),
        'Alias' => array(),
        'Network' => array('10.145.64', '10.145.65', '10.145.66', '10.145.67', '10.145.68',
            '10.145.69', '10.145.70', '10.145.71', '77.92.221', '172.16.0', '172.16.1'),
        'IPv6Network' => array('2a00:e580:244::/48'),
    );

    // Local records
    $DbResult = $this->Database->query('SELECT `NetworkInterface`.*, `NetworkDevice`.`Name` AS `DeviceName` '.
      'FROM `NetworkInterface` '.
      'JOIN `NetworkDevice` ON `NetworkDevice`.`Id` = `NetworkInterface`.`Device` '.
      'WHERE (`NetworkInterface`.`LocalIP` <> "") AND (`NetworkDevice`.`Used` = 1)');
    while ($Interface = $DbResult->fetch_assoc())
    {
      $Name = $Interface['DeviceName'];
      if ($Interface['Name'] != '') $Name .= '-'.$Interface['Name'];
      $LocalDNS['Host'][] = array('Name' => $Name, 'Address' => $Interface['LocalIP'],
          'IPv6' => $Interface['IPv6']);
    }

    // External IPv4 records in internal server
    $DbResult = $this->Database->query('SELECT `NetworkInterface`.*, `NetworkDevice`.`Name` AS `DeviceName` FROM `NetworkInterface` '.
        'JOIN `NetworkDevice` ON `NetworkDevice`.`Id` = `NetworkInterface`.`Device` '.
        'WHERE (`NetworkInterface`.`ExternalIP` <> "") AND (`NetworkDevice`.`Used` = 1)');
    while ($Interface = $DbResult->fetch_assoc())
    {
      $Name = $Interface['DeviceName'];
      if ($Interface['Name'] != '') $Name .= '-'.$Interface['Name'];
      $LocalDNS['Host'][] = array('Name' => $Name.'-ext', 'Address' => $Interface['ExternalIP']);
    }

    // CZFree records in internal server
    /*
     $DbResult = $Database->query('SELECT NetworkInterface.*, NetworkDevice.Name AS DeviceName FROM NetworkInterface '.
     'JOIN NetworkDevice ON NetworkDevice.Id = NetworkInterface.Device WHERE NetworkInterface.CZFreeIP <> ""');
     while ($Interface = $DbResult->fetch_assoc())
     {
     $Name = $Interface['DeviceName'];
     if ($Interface['Name'] != '') $Name .= '-'.$Interface['Name'];
     $LocalDNS['Host'][] = array('Name' => $Name.'-czfree', 'Address' => $Interface['CZFreeIP']);
     }
     */

    // Domain aliases
    $DbResult = $this->Database->select('NetworkDomainAlias', '*');
    while ($Alias = $DbResult->fetch_assoc())
    {
      $LocalDNS['Alias'][] = array('Name' => $Alias['Name'], 'Target' => $Alias['Target']);
    }


    // === External network DNS server ===
    $ExternalDNS = array(
        'Domain' => $BaseDomain,
        'Serial' => $Serial,
        'Refresh' => $RefreshTime,
        'Expire' => $ExpireTime,
        'Retry' => $RetryTime,
        'Minimum' => $MinimumTime,
        'TTL' => $TTL,
        'Description' => 'ZdechovNET community network',
        'BaseDir' => $BaseDir.'/external/',
        'MailServer' => array('mail.'.$BaseDomain),
        'NameServer' => array('gw-hajda.inext.cz', 'ns2.afraid.org', 'ns5.he.net'),
        'BaseServer' => '77.92.221.215', // warp
        'BaseServerIPv6' => '2a00:e580:244:1::7', // warp
        'ReverseNameServer' => array('centrala.'.$BaseDomain, 'mozek.'.$BaseDomain),
        'Host' => array(),
        'Alias' => array(),
        'Network' => array('81.2.194', '193.86.238', '212.111.4', '77.92.221'),
        'IPv6Network' => array('2a00:e580:244::/48'),
    );

    $DbResult = $this->Database->query('SELECT `NetworkInterface`.*, `NetworkDevice`.`Name` AS `DeviceName` FROM `NetworkInterface` '.
        'JOIN `NetworkDevice` ON `NetworkDevice`.`Id` = `NetworkInterface`.`Device` WHERE '.
        '(`NetworkInterface`.`ExternalIP` != "") AND (`NetworkDevice`.`Used` = 1)');
    while ($Interface = $DbResult->fetch_assoc())
    {
      $Name = $Interface['DeviceName'];
      if ($Interface['Name'] != '') $Name .= '-'.$Interface['Name'];
      $ExternalDNS['Host'][] = array('Name' => $Name, 'Address' => $Interface['ExternalIP'],
          'IPv6' => $Interface['IPv6']);
    }

    // Domain alias
    $DbResult = $this->Database->query('SELECT `NetworkDomainAlias`.* FROM `NetworkDomainAlias`');
    // JOIN `NetworkDevice` ON NetworkDomainAlias.Target LIKE NetworkDevice.Name AND NetworkInterface.ExternalIP != ""');
    while ($Alias = $DbResult->fetch_assoc())
    {
      $ExternalDNS['Alias'][] = array('Name' => $Alias['Name'], 'Target' => $Alias['Target']);
    }


    // === CZFree network DNS server ===
    $BaseDomain = 'zdechovnet.czf';
    $CZFreeDNS = array(
        'Domain' => $BaseDomain,
        'Serial' => date('Ymds', time()),
        'Refresh' => 28800,
        'Expire' => 2419200,
        'Retry' => 7200,
        'Minimum' => 10800,
        'TTL' => 86400,
        'Description' => 'ZdechovNET community network',
        'BaseDir' => $BaseDir.'/czfree/',
        'MailServer' => array('mail.'.$BaseDomain),
        'NameServer' => array('centrala.'.$BaseDomain, 'mozek'.$BaseDomain),
        'BaseServer' => '10.145.64.23', // warp
        'BaseServerIPv6' => '2a00:e580:244:1::7', // warp
        'ReverseNameServer' => array('centrala.'.$BaseDomain, 'mozek.'.$BaseDomain),
        'Host' => array(),
        'Alias' => array(),
        'Network' => array('10.145.64', '10.145.65', '10.145.66', '10.145.67',
            '10.145.68', '10.145.69', '10.145.70', '10.145.71'),
        'IPv6Network' => array(),
    );

    // Hosts
    $DbResult = $this->Database->query('SELECT `NetworkInterface`.*, `NetworkDevice`.`Name` AS `DeviceName` FROM `NetworkInterface` '.
        'JOIN `NetworkDevice` ON `NetworkDevice`.`Id` = `NetworkInterface`.`Device` '.
        'WHERE (`NetworkInterface`.`LocalIP` != "") AND (`NetworkDevice`.`Used` = 1)');
    while ($Interface = $DbResult->fetch_assoc())
    {
      $Name = $Interface['DeviceName'];
      if ($Interface['Name'] != '') $Name .= '-'.$Interface['Name'];
      $CZFreeDNS['Host'][] = array('Name' => $Name, 'Address' => $Interface['LocalIP']);
    }

    // Domain alias
    $DbResult = $this->Database->query('SELECT `NetworkDomainAlias`.* FROM `NetworkDomainAlias`');
    // JOIN `hosts` ON NetworkDomainAlias.Target LIKE hosts.name AND hosts.czfree_ip != ""');
    while ($Alias = $DbResult->fetch_assoc())
    {
      $CZFreeDNS['Alias'][] = array('Name' => $Alias['Name'], 'Target' => $Alias['Target']);
    }

    // === CZFree network local address ===
    $BaseDomain = 'zdechovnet.czf';
    $CZFreeLocalDNS = array(
        'Domain' => $BaseDomain,
        'Serial' => date('Ymds', time()),
        'Refresh' => 28800,
        'Expire' => 2419200,
        'Retry' => 7200,
        'Minimum' => 10800,
        'TTL' => 86400,
        'Description' => 'ZdechovNET community network',
        'BaseDir' => $BaseDir.'/internal/',
        'MailServer' => array('mail.'.$BaseDomain),
        'NameServer' => array('centrala.'.$BaseDomain, 'mozek.'.$BaseDomain),
        'BaseServer' => '10.145.64.23', // warp
        'BaseServerIPv6' => '2a00:e580:244:1::7', // warp
        'ReverseNameServer' => array('centrala.'.$BaseDomain, 'mozek.'.$BaseDomain),
        'Host' => array(),
        'Alias' => array(),
        'Network' => array('10.145.64', '10.145.65', '10.145.66', '10.145.67',
            '10.145.68', '10.145.69', '10.145.70', '10.145.71'),
        'IPv6Network' => array(),
    );

    // Hosts
    $DbResult = $this->Database->query('SELECT `NetworkInterface`.*, `NetworkDevice`.`Name` AS `DeviceName` FROM `NetworkInterface` '.
        'JOIN `NetworkDevice` ON `NetworkDevice`.`Id` = `NetworkInterface`.`Device` WHERE `NetworkInterface`.`LocalIP` != ""');
    while ($Interface = $DbResult->fetch_assoc())
    {
      $Name = $Interface['DeviceName'];
      if ($Interface['Name'] != '') $Name .= '-'.$Interface['Name'];
      //  $CZFreeLocalDNS['Host'][] = array('Name' => $Name.'-czfree', 'Address' => $Interface['LocalIP']);
      $CZFreeLocalDNS['Host'][] = array('Name' => $Name, 'Address' => $Interface['LocalIP']);
    }

    // Domain alias
    $DbResult = $this->Database->query('SELECT `NetworkDomainAlias`.* FROM `NetworkDomainAlias`');
    // JOIN `hosts` ON NetworkDomainAlias.Target LIKE hosts.name AND hosts.czfree_ip != ""');
    while ($Alias = $DbResult->fetch_assoc())
    {
      $CZFreeLocalDNS['Alias'][] = array('Name' => $Alias['Name'], 'Target' => $Alias['Target']);
    }

    $this->GenerateDNS($CZFreeDNS);
    $this->GenerateDNS($CZFreeLocalDNS);

    $this->GenerateDNS($LocalDNS);
    $this->GenerateDNS($ExternalDNS);
  }
}
