97 lines
3.7 KiB
PHP
97 lines
3.7 KiB
PHP
<?php declare(strict_types=1);
|
|
|
|
regCheckDomainPossession($_POST['domain']);
|
|
|
|
$suffix = regParseDomain($_POST['domain'])['suffix'];
|
|
|
|
$path = CONF['reg']['suffixes_path'] . '/' . $suffix . 'zone';
|
|
|
|
if (isset($_POST['records'])) {
|
|
|
|
// Generate new content
|
|
$new_records = '';
|
|
if (strlen($_POST['records']) > REG_TEXTAREA_MAX_CHARACTERS)
|
|
output(403, sprintf(_('The zone is limited to %s characters.'), REG_TEXTAREA_MAX_CHARACTERS));
|
|
foreach (explode("\r\n", $_POST['records']) as $record) {
|
|
if ($record === '') continue;
|
|
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', $record, $matches) !== 1)
|
|
output(403, _('The following line does not match the expected format: ') . '<code>' . htmlspecialchars($record) . '</code>');
|
|
if (in_array($matches['type'], REG_ALLOWED_TYPES, true) !== true)
|
|
output(403, sprintf(_('The %s type is not allowed.'), '<code>' . $matches['type'] . '</code>'));
|
|
if ($matches['type'] === 'DS' AND count($record_values = explode(' ', $matches['value'])) !== 4)
|
|
output(403, _('A DS record expects 4 arguments.'));
|
|
$new_records .= implode(' ', regParseRecord($_POST['domain'], [
|
|
'domain' => $matches['domain'],
|
|
'type' => match ($matches['type']) {
|
|
'NS', 'DS' => $matches['type'],
|
|
'AAAA', 'A' => 'ip',
|
|
default => output(403, sprintf(_('The %s type is not allowed.'), '<code>' . $matches['type'] . '</code>')),
|
|
},
|
|
...match ($matches['type']) {
|
|
'NS' => ['ns' => $matches['value']],
|
|
'AAAA', 'A' => ['ip' => $matches['value']],
|
|
'DS' => array_combine([
|
|
'keytag',
|
|
'algo',
|
|
'dt',
|
|
'key',
|
|
], $record_values),
|
|
},
|
|
])) . LF;
|
|
}
|
|
|
|
// Send the zone content to kzonecheck's stdin
|
|
$process = proc_open(CONF['ns']['kzonecheck_path'] . ' --origin ' . escapeshellarg($suffix) . ' --dnssec off -', [0 => ['pipe', 'r']], $pipes);
|
|
if (is_resource($process) !== true)
|
|
output(500, 'Can\'t spawn kzonecheck.');
|
|
$new = $suffix . ' 10800 SOA invalid. invalid. 0 21600 7200 3628800 3600' . LF . $suffix . ' 10800 NS invalid.' . LF . $new_records;
|
|
fwrite($pipes[0], $new);
|
|
fclose($pipes[0]);
|
|
if (proc_close($process) !== 0)
|
|
output(403, _('Sent content is not correct (according to <code>kzonecheck</code>).'));
|
|
|
|
ratelimit();
|
|
|
|
knotc(['zone-freeze', $suffix], $output, $return_code);
|
|
if ($return_code !== 0)
|
|
output(500, 'Failed to freeze zone file.', $output);
|
|
|
|
knotc(['zone-flush', $suffix], $output, $return_code);
|
|
if ($return_code !== 0)
|
|
output(500, 'Failed to flush zone file.', $output);
|
|
|
|
if (($zone_content = file_get_contents($path)) === false)
|
|
output(500, 'Unable to read zone file.');
|
|
|
|
$zone_content = regStripDomain($_POST['domain'], $zone_content);
|
|
|
|
if (file_put_contents($path, $zone_content . LF . $new_records) === false)
|
|
output(500, 'Failed to write zone file.');
|
|
|
|
knotc(['zone-reload', $suffix], $output, $return_code);
|
|
if ($return_code !== 0)
|
|
output(500, 'Failed to reload zone file.', $output);
|
|
|
|
knotc(['zone-thaw', $suffix], $output, $return_code);
|
|
if ($return_code !== 0)
|
|
output(500, 'Failed to thaw zone file.', $output);
|
|
|
|
usleep(1000000);
|
|
}
|
|
|
|
// Display zone
|
|
|
|
if (($records = file_get_contents($path)) === false)
|
|
output(500, 'Unable to read zone file.');
|
|
|
|
$data['records'] = '';
|
|
foreach (explode(LF, $records) as $zone_line) {
|
|
if (empty($zone_line) OR str_starts_with($zone_line, ';'))
|
|
continue;
|
|
if (preg_match('/^(?:(?:[a-z0-9_-]{1,63}\.){1,127})?' . preg_quote($_POST['domain'], '/') . '[\t ]+[0-9]{1,8}[\t ]+(?<type>[A-Z]{1,16})[\t ]+.+$/D', $zone_line, $matches)) {
|
|
if (in_array($matches['type'], REG_ALLOWED_TYPES, true) !== true)
|
|
continue;
|
|
$data['records'] .= $zone_line . LF;
|
|
}
|
|
}
|
|
$data['records'] .= LF;
|