dns.php 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. <?php declare(strict_types=1);
  2. function parseZoneFile(string $zone_content, array $types, bool|string $filter_domain = false, bool $filter_include_subdomains = true): array {
  3. $parsed_zone_content = [];
  4. foreach (explode(LF, $zone_content) as $zone_line) {
  5. if ($zone_line === '' OR str_starts_with($zone_line, ';'))
  6. continue; // Ignore empty lines and comments
  7. $elements = preg_split('/[\t ]+/', $zone_line, 4);
  8. if ($filter_domain !== false AND match ($filter_include_subdomains) {
  9. true => !str_ends_with($elements[0], $filter_domain),
  10. false => $elements[0] !== $filter_domain,
  11. })
  12. continue; // Ignore records for other domains
  13. if (!in_array($elements[2], $types, true))
  14. continue; // Ignore records generated by Knot
  15. array_push($parsed_zone_content, $elements);
  16. }
  17. return $parsed_zone_content;
  18. }
  19. function knotc(array $cmds, array &$output = NULL, int &$return_code = NULL): void {
  20. exescape([
  21. CONF['dns']['knotc_path'],
  22. '--blocking',
  23. '--timeout',
  24. '20',
  25. '--',
  26. ...$cmds,
  27. ], $output, $return_code);
  28. }
  29. function knotcConfExec(array $cmds): void {
  30. knotc(['conf-begin'], $output['begin'], $code['begin']);
  31. if ($code['begin'] !== 0)
  32. output(500, 'knotcConfExec: <code>knotc</code> failed with exit code <samp>' . $code['begin'] . '</samp>: <samp>' . $output['begin'][0] . '</samp>.');
  33. foreach ($cmds as $cmd) {
  34. knotc($cmd, $output['op'], $code['op']);
  35. if ($code['op'] !== 0) {
  36. knotc(['conf-abort']);
  37. output(500, 'knotcConfExec: <code>knotc</code> failed with exit code <samp>' . $code['op'] . '</samp>: <samp>' . $output['op'][0] . '</samp>.');
  38. }
  39. }
  40. knotc(['conf-commit'], $output['commit'], $code['commit']);
  41. if ($code['commit'] !== 0) {
  42. knotc(['conf-abort']);
  43. output(500, 'knotcConfExec: <code>knotc</code> failed with exit code <samp>' . $code['commit'] . '</samp>: <samp>' . $output['commit'][0] . '</samp>.');
  44. }
  45. }
  46. function knotcZoneExec(string $zone, array $cmd, string $action = NULL): void {
  47. $action = checkAction($action ?? $_POST['action']);
  48. knotc(['zone-begin', $zone], $output['begin'], $code['begin']);
  49. if ($code['begin'] !== 0)
  50. output(500, 'knotcZoneExec: <code>knotc</code> failed with exit code <samp>' . $code['begin'] . '</samp>: <samp>' . $output['begin'][0] . '</samp>.');
  51. knotc(['zone-' . $action . 'set', $zone, ...$cmd], $output['op'], $code['op']);
  52. if ($code['op'] !== 0) {
  53. knotc(['zone-abort', $zone]);
  54. output(500, 'knotcZoneExec: <code>knotc</code> failed with exit code <samp>' . $code['op'] . '</samp>: <samp>' . $output['op'][0] . '</samp>.');
  55. }
  56. knotc(['zone-commit', $zone], $output['commit'], $code['commit']);
  57. if ($code['commit'] !== 0) {
  58. knotc(['zone-abort', $zone]);
  59. output(500, 'knotcZoneExec: <code>knotc</code> failed with exit code <samp>' . $code['commit'] . '</samp>: <samp>' . $output['commit'][0] . '</samp>.');
  60. }
  61. }
  62. function checkIpFormat(string $ip): string {
  63. return match ($ip) {
  64. filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) => 'AAAA',
  65. filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) => 'A',
  66. default => output(403, _('IP address malformed.')),
  67. };
  68. }
  69. function checkAbsoluteDomainFormat(string $domain): void { // If the domain must end with a dot
  70. if (!filter_var($domain, FILTER_VALIDATE_DOMAIN) OR preg_match('/^(?=^.{1,254}$)([a-z0-9_-]{1,63}\.){2,127}$/D', $domain) !== 1)
  71. output(403, _('Domain malformed.'));
  72. }
  73. function formatEndWithDot(string $str): string {
  74. if (!str_ends_with($str, '.'))
  75. $str .= '.';
  76. return $str;
  77. }
  78. function formatAbsoluteDomain(string $domain): string {
  79. $domain = formatEndWithDot(strtolower($domain));
  80. checkAbsoluteDomainFormat($domain);
  81. return $domain;
  82. }
  83. function checkAction(string $action): string {
  84. return match ($action) {
  85. 'add' => '',
  86. 'delete' => 'un',
  87. default => output(403, 'Wrong value for action.'),
  88. };
  89. }