2022-11-20 00:05:03 +00:00
|
|
|
<?php
|
|
|
|
|
2022-12-20 20:17:03 +00:00
|
|
|
nsCheckZonePossession($_POST['zone']);
|
|
|
|
|
|
|
|
if (isset($_POST['zone-content'])) { // Update zone
|
2022-11-20 00:05:03 +00:00
|
|
|
|
|
|
|
// Get current SOA record
|
|
|
|
$current_zone_content = file_get_contents(CONF['ns']['knot_zones_path'] . '/' . $_POST['zone'] . 'zone');
|
|
|
|
if ($current_zone_content === false)
|
|
|
|
output(500, 'Unable to read zone file.');
|
2022-11-20 17:17:03 +00:00
|
|
|
if (preg_match('/^(?<soa>' . preg_quote($_POST['zone'], '/') . '[\t ]+[0-9]{1,16}[\t ]+SOA[\t ]+.+)$/Dm', $current_zone_content, $matches) !== 1)
|
|
|
|
output(500, 'Unable to get current SOA record from zone file.');
|
2022-11-20 00:05:03 +00:00
|
|
|
|
|
|
|
// Generate new zone content
|
2022-11-20 14:11:54 +00:00
|
|
|
$new_zone_content = $matches['soa'] . LF;
|
2022-11-20 00:05:03 +00:00
|
|
|
if (strlen($_POST['zone-content']) > ZONE_MAX_CHARACTERS)
|
2023-01-21 00:27:52 +00:00
|
|
|
output(403, sprintf(_('The zone is limited to %s characters.'), ZONE_MAX_CHARACTERS));
|
2022-11-20 00:05:03 +00:00
|
|
|
foreach (explode("\r\n", $_POST['zone-content']) as $line) {
|
|
|
|
if ($line === '') continue;
|
2023-04-21 17:01:46 +00:00
|
|
|
if (preg_match('/^(?<domain>[a-z0-9@._-]{1,256})(?:[\t ]+(?<ttl>[0-9]{1,16}))?(?:[\t ]+IN)?[\t ]+(?<type>[A-Z]{1,16})[\t ]+(?<value>.+)$/D', $line, $matches) !== 1)
|
2023-01-21 00:27:52 +00:00
|
|
|
output(403, _('The following line does not match the expected format: ') . '<code>' . htmlspecialchars($line) . '</code>');
|
2022-11-20 00:05:03 +00:00
|
|
|
if (in_array($matches['type'], ALLOWED_TYPES, true) !== true)
|
2023-01-21 00:27:52 +00:00
|
|
|
output(403, sprintf(_('The %s type is not allowed.'), '<code>' . $matches['type'] . '</code>'));
|
2022-11-20 00:05:03 +00:00
|
|
|
if ($matches['ttl'] !== '' AND $matches['ttl'] < MIN_TTL)
|
2023-01-21 00:27:52 +00:00
|
|
|
output(403, sprintf(_('TTLs shorter than %s seconds are forbidden.'), MIN_TTL));
|
2022-11-20 00:05:03 +00:00
|
|
|
if ($matches['ttl'] !== '' AND $matches['ttl'] > MAX_TTL)
|
2023-01-21 00:27:52 +00:00
|
|
|
output(403, sprintf(_('TTLs longer than %s seconds are forbidden.'), MAX_TTL));
|
2022-11-20 14:11:54 +00:00
|
|
|
$new_zone_content .= $matches['domain'] . ' ' . (($matches['ttl'] === '') ? DEFAULT_TTL : $matches['ttl']) . ' ' . $matches['type'] . ' ' . $matches['value'] . LF;
|
2022-11-20 00:05:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Send the zone content to kzonecheck's stdin
|
|
|
|
$process = proc_open(CONF['ns']['kzonecheck_path'] . ' --origin ' . $_POST['zone'] . ' --dnssec off -', [0 => ['pipe', 'r']], $pipes);
|
|
|
|
if (is_resource($process) !== true)
|
|
|
|
output(500, 'Can\'t spawn kzonecheck.');
|
|
|
|
fwrite($pipes[0], $new_zone_content);
|
|
|
|
fclose($pipes[0]);
|
|
|
|
if (proc_close($process) !== 0)
|
2023-01-21 00:27:52 +00:00
|
|
|
output(403, _('Sent zone content is not correct (according to <code>kzonecheck</code>).'));
|
2022-11-20 00:05:03 +00:00
|
|
|
|
|
|
|
ratelimit();
|
|
|
|
|
2023-06-19 00:15:43 +00:00
|
|
|
knotc(['zone-freeze', $_POST['zone']], $output, $return_code);
|
2022-11-20 00:05:03 +00:00
|
|
|
if ($return_code !== 0)
|
|
|
|
output(500, 'Failed to freeze zone file.', $output);
|
|
|
|
|
2023-06-19 00:15:43 +00:00
|
|
|
knotc(['zone-flush', $_POST['zone']], $output, $return_code);
|
2022-11-20 00:05:03 +00:00
|
|
|
if ($return_code !== 0)
|
|
|
|
output(500, 'Failed to flush zone file.', $output);
|
|
|
|
|
|
|
|
if (file_put_contents(CONF['ns']['knot_zones_path'] . '/' . $_POST['zone'] . 'zone', $new_zone_content) === false)
|
|
|
|
output(500, 'Failed to write zone file.');
|
|
|
|
|
2023-06-19 00:15:43 +00:00
|
|
|
knotc(['zone-reload', $_POST['zone']], $output, $return_code);
|
2022-11-20 00:05:03 +00:00
|
|
|
if ($return_code !== 0)
|
|
|
|
output(500, 'Failed to reload zone file.', $output);
|
|
|
|
|
2023-06-19 00:15:43 +00:00
|
|
|
knotc(['zone-thaw', $_POST['zone']], $output, $return_code);
|
2022-11-20 00:05:03 +00:00
|
|
|
if ($return_code !== 0)
|
|
|
|
output(500, 'Failed to thaw zone file.', $output);
|
|
|
|
|
|
|
|
usleep(1000000);
|
|
|
|
}
|
|
|
|
|
2022-12-20 20:17:03 +00:00
|
|
|
// Display zone
|
2022-11-20 00:05:03 +00:00
|
|
|
|
2022-12-20 20:17:03 +00:00
|
|
|
$zone_content = file_get_contents(CONF['ns']['knot_zones_path'] . '/' . $_POST['zone'] . 'zone');
|
|
|
|
if ($zone_content === false)
|
|
|
|
output(500, 'Unable to read zone file.');
|
2022-11-20 00:05:03 +00:00
|
|
|
|
2022-12-20 20:17:03 +00:00
|
|
|
$data['zone_content'] = '';
|
2023-01-23 00:14:59 +00:00
|
|
|
foreach (explode(LF, $zone_content) as $zone_line) {
|
2022-12-20 20:17:03 +00:00
|
|
|
if (empty($zone_line) OR str_starts_with($zone_line, ';'))
|
|
|
|
continue;
|
|
|
|
if (preg_match('/^(?:(?:[a-z0-9_-]{1,63}\.){1,127})?' . preg_quote($_POST['zone'], '/') . '[\t ]+[0-9]{1,8}[\t ]+(?<type>[A-Z]{1,16})[\t ]+.+$/D', $zone_line, $matches)) {
|
|
|
|
if (in_array($matches['type'], ALLOWED_TYPES, true) !== true)
|
2022-11-20 00:05:03 +00:00
|
|
|
continue;
|
2022-12-20 20:17:03 +00:00
|
|
|
$data['zone_content'] .= $zone_line . LF;
|
2022-11-20 00:05:03 +00:00
|
|
|
}
|
|
|
|
}
|
2022-12-20 20:17:03 +00:00
|
|
|
$data['zone_content'] .= LF;
|