Add reg/edit.php and regParseRecord()
This commit is contained in:
parent
40e67b0c0c
commit
2570d09ba9
16 changed files with 899 additions and 634 deletions
10
fn/dns.php
10
fn/dns.php
|
@ -70,11 +70,11 @@ function knotcZoneExec(string $zone, array $cmd, string $action = NULL): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkIpFormat(string $ip): string {
|
function checkIpFormat(string $ip): string {
|
||||||
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4))
|
return match ($ip) {
|
||||||
return 'A';
|
filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) => 'AAAA',
|
||||||
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6))
|
filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) => 'A',
|
||||||
return 'AAAA';
|
default => output(403, _('IP address malformed.')),
|
||||||
output(403, _('IP address malformed.'));
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkAbsoluteDomainFormat(string $domain): void { // If the domain must end with a dot
|
function checkAbsoluteDomainFormat(string $domain): void { // If the domain must end with a dot
|
||||||
|
|
22
fn/ns.php
22
fn/ns.php
|
@ -1,6 +1,6 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
const SOA_VALUES = [
|
const NS_SOA_VALUES = [
|
||||||
'ttl' => 10800,
|
'ttl' => 10800,
|
||||||
'email' => CONF['ns']['public_soa_email'],
|
'email' => CONF['ns']['public_soa_email'],
|
||||||
'refresh' => 10800,
|
'refresh' => 10800,
|
||||||
|
@ -9,15 +9,15 @@ const SOA_VALUES = [
|
||||||
'negative' => 10800,
|
'negative' => 10800,
|
||||||
];
|
];
|
||||||
|
|
||||||
const MIN_TTL = 300;
|
const NS_MIN_TTL = 300;
|
||||||
const DEFAULT_TTL = 10800;
|
const NS_DEFAULT_TTL = 10800;
|
||||||
const MAX_TTL = 1728000;
|
const NS_MAX_TTL = 1728000;
|
||||||
|
|
||||||
const ALLOWED_TYPES = ['AAAA', 'A', 'TXT', 'SRV', 'MX', 'SSHFP', 'TLSA', 'NS', 'DS', 'CSYNC', 'CAA', 'CNAME', 'DNAME', 'SVCB', 'HTTPS', 'LOC'];
|
const NS_ALLOWED_TYPES = ['AAAA', 'A', 'TXT', 'SRV', 'MX', 'SSHFP', 'TLSA', 'NS', 'DS', 'CSYNC', 'CAA', 'CNAME', 'DNAME', 'SVCB', 'HTTPS', 'LOC'];
|
||||||
|
|
||||||
const ZONE_MAX_CHARACTERS = 10000;
|
const NS_TEXTAREA_MAX_CHARACTERS = 10000;
|
||||||
|
|
||||||
const SYNC_TTL = 10800;
|
const NS_SYNC_TTL = 10800;
|
||||||
|
|
||||||
function nsParseCommonRequirements(): array {
|
function nsParseCommonRequirements(): array {
|
||||||
nsCheckZonePossession($_POST['zone']);
|
nsCheckZonePossession($_POST['zone']);
|
||||||
|
@ -29,10 +29,10 @@ function nsParseCommonRequirements(): array {
|
||||||
|
|
||||||
$values['ttl'] = intval($_POST['ttl-value'] * $_POST['ttl-multiplier']);
|
$values['ttl'] = intval($_POST['ttl-value'] * $_POST['ttl-multiplier']);
|
||||||
|
|
||||||
if ($values['ttl'] < MIN_TTL)
|
if ($values['ttl'] < NS_MIN_TTL)
|
||||||
output(403, sprintf(_('TTLs shorter than %s seconds are forbidden.'), MIN_TTL));
|
output(403, sprintf(_('TTLs shorter than %s seconds are forbidden.'), NS_MIN_TTL));
|
||||||
if ($values['ttl'] > MAX_TTL)
|
if ($values['ttl'] > NS_MAX_TTL)
|
||||||
output(403, sprintf(_('TTLs longer than %s seconds are forbidden.'), MAX_TTL));
|
output(403, sprintf(_('TTLs longer than %s seconds are forbidden.'), NS_MAX_TTL));
|
||||||
|
|
||||||
return $values;
|
return $values;
|
||||||
}
|
}
|
||||||
|
|
63
fn/reg.php
63
fn/reg.php
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
const SUBDOMAIN_REGEX = '^(?!\-)(?!..\-\-)[a-z0-9-]{4,63}(?<!\-)$';
|
const SUBDOMAIN_REGEX = '^(?!\-)(?!..\-\-)[a-z0-9-]{4,63}(?<!\-)$';
|
||||||
|
|
||||||
|
const REG_TEXTAREA_MAX_CHARACTERS = 5000;
|
||||||
|
const REG_ALLOWED_TYPES = ['NS', 'DS', 'AAAA', 'A'];
|
||||||
|
|
||||||
function regListUserDomains(): array {
|
function regListUserDomains(): array {
|
||||||
if (isset($_SESSION['id']))
|
if (isset($_SESSION['id']))
|
||||||
return query('select', 'registry', ['username' => $_SESSION['id']], 'domain');
|
return query('select', 'registry', ['username' => $_SESSION['id']], 'domain');
|
||||||
|
@ -13,14 +16,16 @@ function regCheckDomainPossession(string $domain): void {
|
||||||
output(403, 'You don\'t own this domain on the registry.');
|
output(403, 'You don\'t own this domain on the registry.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function regStripDomain(string $domain, string $content): string {
|
||||||
|
return preg_replace('/^(?:[a-z0-9._-]+\.)?' . preg_quote($domain, '/') . '[\t ]+.+$/Dm', '', $content);
|
||||||
|
}
|
||||||
|
|
||||||
function regDeleteDomain(string $domain, string $user_id): void {
|
function regDeleteDomain(string $domain, string $user_id): void {
|
||||||
// Delete domain from registry file
|
// Delete domain from registry file
|
||||||
$path = CONF['reg']['suffixes_path'] . '/' . regParseDomain($domain)['suffix'] . 'zone';
|
$path = CONF['reg']['suffixes_path'] . '/' . regParseDomain($domain)['suffix'] . 'zone';
|
||||||
$content = file_get_contents($path);
|
if (($content = file_get_contents($path)) === false)
|
||||||
if ($content === false)
|
|
||||||
output(500, 'Failed to read current registry file.');
|
output(500, 'Failed to read current registry file.');
|
||||||
$content = preg_replace('/^(?:[a-z0-9._-]+\.)?' . preg_quote($domain, '/') . '[\t ]+.+$/Dm', '', $content);
|
if (file_put_contents($path, regStripDomain($domain, $content)) === false)
|
||||||
if (file_put_contents($path, $content) === false)
|
|
||||||
output(500, 'Failed to write new registry file.');
|
output(500, 'Failed to write new registry file.');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -59,3 +64,53 @@ function regParseDomain(string $domain): array {
|
||||||
'suffix' => $suffix,
|
'suffix' => $suffix,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function regParseRecord(string $domain, array $record): array {
|
||||||
|
checkAbsoluteDomainFormat($record['domain']);
|
||||||
|
|
||||||
|
if ($record['domain'] !== $_POST['domain']) {
|
||||||
|
if ($record['type'] !== 'ip')
|
||||||
|
output(403, _('You can only set a NS/DS record for an apex domain.'));
|
||||||
|
else if (!str_ends_with($record['domain'], '.' . $domain))
|
||||||
|
output(403, _('You can\'t set a record for another domain.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$new_rec = [
|
||||||
|
$record['domain'],
|
||||||
|
CONF['reg']['ttl'],
|
||||||
|
$record['type'],
|
||||||
|
];
|
||||||
|
if ($record['type'] === 'DS') {
|
||||||
|
if (!in_array($record['algo'], ['8', '13', '14', '15', '16'], true))
|
||||||
|
output(403, 'Wrong value for <code>algo</code>.');
|
||||||
|
|
||||||
|
if ((preg_match('/^[0-9]{1,6}$/D', $record['keytag'])) !== 1 OR !($record['keytag'] >= 1) OR !($record['keytag'] <= 65535))
|
||||||
|
output(403, 'Wrong value for <code>keytag</code>.');
|
||||||
|
|
||||||
|
if ($record['dt'] !== '2' AND $record['dt'] !== '4')
|
||||||
|
output(403, 'Wrong value for <code>dt</code>.');
|
||||||
|
|
||||||
|
if (preg_match('/^(?:[0-9a-fA-F]{64}|[0-9a-fA-F]{96})$/D', $record['key']) !== 1)
|
||||||
|
output(403, 'Wrong value for <code>key</code>.');
|
||||||
|
|
||||||
|
return [
|
||||||
|
...$new_rec,
|
||||||
|
$record['keytag'],
|
||||||
|
$record['algo'],
|
||||||
|
$record['dt'],
|
||||||
|
$record['key'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if ($record['type'] === 'NS')
|
||||||
|
return [
|
||||||
|
...$new_rec,
|
||||||
|
formatAbsoluteDomain($record['ns']),
|
||||||
|
];
|
||||||
|
if ($record['type'] === 'ip')
|
||||||
|
return [
|
||||||
|
$record['domain'],
|
||||||
|
CONF['reg']['ttl'],
|
||||||
|
checkIpFormat($record['ip']),
|
||||||
|
$record['ip'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
|
@ -204,8 +204,8 @@ function testNs(string $domain): void {
|
||||||
exit('Error: /ns/caa: CAA record not set' . LF);
|
exit('Error: /ns/caa: CAA record not set' . LF);
|
||||||
|
|
||||||
curlTest('/ns/edit', [
|
curlTest('/ns/edit', [
|
||||||
'zone' => $domain,
|
'domain' => $domain,
|
||||||
'zone-content' => 'aaaa.' . $domain . ' 3600 AAAA ' . CONF['ht']['ipv6_address'] . "\r\n"
|
'records' => 'aaaa.' . $domain . ' 3600 AAAA ' . CONF['ht']['ipv6_address'] . "\r\n"
|
||||||
. '@ 86400 NS ' . CONF['ns']['servers'][0] . "\r\n",
|
. '@ 86400 NS ' . CONF['ns']['servers'][0] . "\r\n",
|
||||||
]);
|
]);
|
||||||
exescape([
|
exescape([
|
||||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -63,6 +63,11 @@ define('PAGES', [
|
||||||
'description' => _('Print every record related to a domain and served by the registry'),
|
'description' => _('Print every record related to a domain and served by the registry'),
|
||||||
'tokens_account_cost' => 60,
|
'tokens_account_cost' => 60,
|
||||||
],
|
],
|
||||||
|
'edit' => [
|
||||||
|
'title' => _('Edit records'),
|
||||||
|
'description' => _('Set registry records to delegate a domain to chosen name servers'),
|
||||||
|
'tokens_account_cost' => 900,
|
||||||
|
],
|
||||||
'ns' => [
|
'ns' => [
|
||||||
'title' => sprintf(_('%s records'), '<abbr title="Name Server">NS</abbr>'),
|
'title' => sprintf(_('%s records'), '<abbr title="Name Server">NS</abbr>'),
|
||||||
'description' => sprintf(_('Indicate the name servers of a %s subdomain'), '<code>' . key(CONF['reg']['suffixes']) . '</code>'),
|
'description' => sprintf(_('Indicate the name servers of a %s subdomain'), '<code>' . key(CONF['reg']['suffixes']) . '</code>'),
|
||||||
|
|
|
@ -1,60 +1,62 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
nsCheckZonePossession($_POST['zone']);
|
nsCheckZonePossession($_POST['domain']);
|
||||||
|
|
||||||
if (isset($_POST['zone-content'])) { // Update zone
|
$path = CONF['ns']['knot_zones_path'] . '/' . $_POST['domain'] . 'zone';
|
||||||
|
|
||||||
|
if (isset($_POST['records'])) {
|
||||||
|
|
||||||
// Get current SOA record
|
// Get current SOA record
|
||||||
$current_zone_content = file_get_contents(CONF['ns']['knot_zones_path'] . '/' . $_POST['zone'] . 'zone');
|
$current_zone_content = file_get_contents($path);
|
||||||
if ($current_zone_content === false)
|
if ($current_zone_content === false)
|
||||||
output(500, 'Unable to read zone file.');
|
output(500, 'Unable to read zone file.');
|
||||||
if (preg_match('/^(?<soa>' . preg_quote($_POST['zone'], '/') . '[\t ]+[0-9]{1,16}[\t ]+SOA[\t ]+.+)$/Dm', $current_zone_content, $matches) !== 1)
|
if (preg_match('/^(?<soa>' . preg_quote($_POST['domain'], '/') . '[\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.');
|
output(500, 'Unable to get current SOA record from zone file.');
|
||||||
|
|
||||||
// Generate new zone content
|
// Generate new content
|
||||||
$new_zone_content = $matches['soa'] . LF;
|
$new_zone_content = $matches['soa'] . LF;
|
||||||
if (strlen($_POST['zone-content']) > ZONE_MAX_CHARACTERS)
|
if (strlen($_POST['records']) > NS_TEXTAREA_MAX_CHARACTERS)
|
||||||
output(403, sprintf(_('The zone is limited to %s characters.'), ZONE_MAX_CHARACTERS));
|
output(403, sprintf(_('The zone is limited to %s characters.'), NS_TEXTAREA_MAX_CHARACTERS));
|
||||||
foreach (explode("\r\n", $_POST['zone-content']) as $line) {
|
foreach (explode("\r\n", $_POST['records']) as $record) {
|
||||||
if ($line === '') continue;
|
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', $line, $matches) !== 1)
|
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($line) . '</code>');
|
output(403, _('The following line does not match the expected format: ') . '<code>' . htmlspecialchars($record) . '</code>');
|
||||||
if (in_array($matches['type'], ALLOWED_TYPES, true) !== true)
|
if (in_array($matches['type'], NS_ALLOWED_TYPES, true) !== true)
|
||||||
output(403, sprintf(_('The %s type is not allowed.'), '<code>' . $matches['type'] . '</code>'));
|
output(403, sprintf(_('The %s type is not allowed.'), '<code>' . $matches['type'] . '</code>'));
|
||||||
if ($matches['ttl'] !== '' AND $matches['ttl'] < MIN_TTL)
|
if ($matches['ttl'] !== '' AND $matches['ttl'] < NS_MIN_TTL)
|
||||||
output(403, sprintf(_('TTLs shorter than %s seconds are forbidden.'), MIN_TTL));
|
output(403, sprintf(_('TTLs shorter than %s seconds are forbidden.'), NS_MIN_TTL));
|
||||||
if ($matches['ttl'] !== '' AND $matches['ttl'] > MAX_TTL)
|
if ($matches['ttl'] !== '' AND $matches['ttl'] > NS_MAX_TTL)
|
||||||
output(403, sprintf(_('TTLs longer than %s seconds are forbidden.'), MAX_TTL));
|
output(403, sprintf(_('TTLs longer than %s seconds are forbidden.'), NS_MAX_TTL));
|
||||||
$new_zone_content .= $matches['domain'] . ' ' . (($matches['ttl'] === '') ? DEFAULT_TTL : $matches['ttl']) . ' ' . $matches['type'] . ' ' . $matches['value'] . LF;
|
$new_zone_content .= $matches['domain'] . ' ' . (($matches['ttl'] === '') ? NS_DEFAULT_TTL : $matches['ttl']) . ' ' . $matches['type'] . ' ' . $matches['value'] . LF;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the zone content to kzonecheck's stdin
|
// Send the zone content to kzonecheck's stdin
|
||||||
$process = proc_open(CONF['ns']['kzonecheck_path'] . ' --origin ' . $_POST['zone'] . ' --dnssec off -', [0 => ['pipe', 'r']], $pipes);
|
$process = proc_open(CONF['ns']['kzonecheck_path'] . ' --origin ' . escapeshellarg($_POST['domain']) . ' --dnssec off -', [0 => ['pipe', 'r']], $pipes);
|
||||||
if (is_resource($process) !== true)
|
if (is_resource($process) !== true)
|
||||||
output(500, 'Can\'t spawn kzonecheck.');
|
output(500, 'Can\'t spawn kzonecheck.');
|
||||||
fwrite($pipes[0], $new_zone_content);
|
fwrite($pipes[0], $new_zone_content);
|
||||||
fclose($pipes[0]);
|
fclose($pipes[0]);
|
||||||
if (proc_close($process) !== 0)
|
if (proc_close($process) !== 0)
|
||||||
output(403, _('Sent zone content is not correct (according to <code>kzonecheck</code>).'));
|
output(403, _('Sent content is not correct (according to <code>kzonecheck</code>).'));
|
||||||
|
|
||||||
ratelimit();
|
ratelimit();
|
||||||
|
|
||||||
knotc(['zone-freeze', $_POST['zone']], $output, $return_code);
|
knotc(['zone-freeze', $_POST['domain']], $output, $return_code);
|
||||||
if ($return_code !== 0)
|
if ($return_code !== 0)
|
||||||
output(500, 'Failed to freeze zone file.', $output);
|
output(500, 'Failed to freeze zone file.', $output);
|
||||||
|
|
||||||
knotc(['zone-flush', $_POST['zone']], $output, $return_code);
|
knotc(['zone-flush', $_POST['domain']], $output, $return_code);
|
||||||
if ($return_code !== 0)
|
if ($return_code !== 0)
|
||||||
output(500, 'Failed to flush zone file.', $output);
|
output(500, 'Failed to flush zone file.', $output);
|
||||||
|
|
||||||
if (file_put_contents(CONF['ns']['knot_zones_path'] . '/' . $_POST['zone'] . 'zone', $new_zone_content) === false)
|
if (file_put_contents($path, $new_zone_content) === false)
|
||||||
output(500, 'Failed to write zone file.');
|
output(500, 'Failed to write zone file.');
|
||||||
|
|
||||||
knotc(['zone-reload', $_POST['zone']], $output, $return_code);
|
knotc(['zone-reload', $_POST['domain']], $output, $return_code);
|
||||||
if ($return_code !== 0)
|
if ($return_code !== 0)
|
||||||
output(500, 'Failed to reload zone file.', $output);
|
output(500, 'Failed to reload zone file.', $output);
|
||||||
|
|
||||||
knotc(['zone-thaw', $_POST['zone']], $output, $return_code);
|
knotc(['zone-thaw', $_POST['domain']], $output, $return_code);
|
||||||
if ($return_code !== 0)
|
if ($return_code !== 0)
|
||||||
output(500, 'Failed to thaw zone file.', $output);
|
output(500, 'Failed to thaw zone file.', $output);
|
||||||
|
|
||||||
|
@ -63,18 +65,17 @@ if (isset($_POST['zone-content'])) { // Update zone
|
||||||
|
|
||||||
// Display zone
|
// Display zone
|
||||||
|
|
||||||
$zone_content = file_get_contents(CONF['ns']['knot_zones_path'] . '/' . $_POST['zone'] . 'zone');
|
if (($records = file_get_contents($path)) === false)
|
||||||
if ($zone_content === false)
|
|
||||||
output(500, 'Unable to read zone file.');
|
output(500, 'Unable to read zone file.');
|
||||||
|
|
||||||
$data['zone_content'] = '';
|
$data['records'] = '';
|
||||||
foreach (explode(LF, $zone_content) as $zone_line) {
|
foreach (explode(LF, $records) as $zone_line) {
|
||||||
if (empty($zone_line) OR str_starts_with($zone_line, ';'))
|
if (empty($zone_line) OR str_starts_with($zone_line, ';'))
|
||||||
continue;
|
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 (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'], ALLOWED_TYPES, true) !== true)
|
if (in_array($matches['type'], NS_ALLOWED_TYPES, true) !== true)
|
||||||
continue;
|
continue;
|
||||||
$data['zone_content'] .= $zone_line . LF;
|
$data['records'] .= $zone_line . LF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$data['zone_content'] .= LF;
|
$data['records'] .= LF;
|
||||||
|
|
|
@ -41,15 +41,15 @@ insert('zones', [
|
||||||
$knotZonePath = CONF['ns']['knot_zones_path'] . '/' . $_POST['domain'] . 'zone';
|
$knotZonePath = CONF['ns']['knot_zones_path'] . '/' . $_POST['domain'] . 'zone';
|
||||||
$knotZone = implode(' ', [
|
$knotZone = implode(' ', [
|
||||||
$_POST['domain'],
|
$_POST['domain'],
|
||||||
SOA_VALUES['ttl'],
|
NS_SOA_VALUES['ttl'],
|
||||||
'SOA',
|
'SOA',
|
||||||
CONF['ns']['servers'][0],
|
CONF['ns']['servers'][0],
|
||||||
SOA_VALUES['email'],
|
NS_SOA_VALUES['email'],
|
||||||
1,
|
1,
|
||||||
SOA_VALUES['refresh'],
|
NS_SOA_VALUES['refresh'],
|
||||||
SOA_VALUES['retry'],
|
NS_SOA_VALUES['retry'],
|
||||||
SOA_VALUES['expire'],
|
NS_SOA_VALUES['expire'],
|
||||||
SOA_VALUES['negative'],
|
NS_SOA_VALUES['negative'],
|
||||||
]) . LF;
|
]) . LF;
|
||||||
foreach (CONF['ns']['servers'] as $server)
|
foreach (CONF['ns']['servers'] as $server)
|
||||||
$knotZone .= $_POST['domain'] . ' 86400 NS ' . $server . LF;
|
$knotZone .= $_POST['domain'] . ' 86400 NS ' . $server . LF;
|
||||||
|
|
|
@ -1,30 +1,12 @@
|
||||||
<?php declare(strict_types=1);
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
if (!in_array($_POST['algo'], ['8', '13', '14', '15', '16'], true))
|
|
||||||
output(403, 'Wrong value for <code>algo</code>.');
|
|
||||||
|
|
||||||
$_POST['keytag'] = intval($_POST['keytag']);
|
|
||||||
if ((preg_match('/^[0-9]{1,6}$/D', $_POST['keytag'])) !== 1 OR !($_POST['keytag'] >= 1) OR !($_POST['keytag'] <= 65535))
|
|
||||||
output(403, 'Wrong value for <code>keytag</code>.');
|
|
||||||
|
|
||||||
if ($_POST['dt'] !== '2' AND $_POST['dt'] !== '4')
|
|
||||||
output(403, 'Wrong value for <code>dt</code>.');
|
|
||||||
|
|
||||||
if (preg_match('/^(?:[0-9a-fA-F]{64}|[0-9a-fA-F]{96})$/D', $_POST['key']) !== 1)
|
|
||||||
output(403, 'Wrong value for <code>key</code>.');
|
|
||||||
|
|
||||||
regCheckDomainPossession($_POST['domain']);
|
regCheckDomainPossession($_POST['domain']);
|
||||||
|
|
||||||
rateLimit();
|
rateLimit();
|
||||||
|
|
||||||
knotcZoneExec(regParseDomain($_POST['domain'])['suffix'], [
|
knotcZoneExec(regParseDomain($_POST['domain'])['suffix'], regParseRecord($_POST['domain'], [
|
||||||
$_POST['domain'],
|
'type' => 'DS',
|
||||||
CONF['reg']['ttl'],
|
...$_POST,
|
||||||
'DS',
|
]));
|
||||||
$_POST['keytag'],
|
|
||||||
$_POST['algo'],
|
|
||||||
$_POST['dt'],
|
|
||||||
$_POST['key']
|
|
||||||
]);
|
|
||||||
|
|
||||||
output(200, _('Modification done.'));
|
output(200, _('Modification done.'));
|
||||||
|
|
97
pg-act/reg/edit.php
Normal file
97
pg-act/reg/edit.php
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
<?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;
|
|
@ -4,11 +4,10 @@ regCheckDomainPossession($_POST['domain']);
|
||||||
|
|
||||||
rateLimit();
|
rateLimit();
|
||||||
|
|
||||||
knotcZoneExec(regParseDomain($_POST['domain'])['suffix'], [
|
knotcZoneExec(regParseDomain($_POST['domain'])['suffix'], regParseRecord($_POST['domain'], [
|
||||||
formatAbsoluteDomain(formatEndWithDot($_POST['subdomain']) . $_POST['domain']),
|
'type' => 'ip',
|
||||||
CONF['reg']['ttl'],
|
'domain' => formatAbsoluteDomain(formatEndWithDot($_POST['subdomain']) . $_POST['domain']),
|
||||||
checkIpFormat($_POST['ip']),
|
...$_POST,
|
||||||
$_POST['ip']
|
]));
|
||||||
]);
|
|
||||||
|
|
||||||
output(200, _('Modification done.'));
|
output(200, _('Modification done.'));
|
||||||
|
|
|
@ -4,11 +4,9 @@ regCheckDomainPossession($_POST['domain']);
|
||||||
|
|
||||||
rateLimit();
|
rateLimit();
|
||||||
|
|
||||||
knotcZoneExec(regParseDomain($_POST['domain'])['suffix'], [
|
knotcZoneExec(regParseDomain($_POST['domain'])['suffix'], regParseRecord($_POST['domain'], [
|
||||||
$_POST['domain'],
|
'type' => 'NS',
|
||||||
CONF['reg']['ttl'],
|
...$_POST,
|
||||||
'NS',
|
]));
|
||||||
formatAbsoluteDomain($_POST['ns'])
|
|
||||||
]);
|
|
||||||
|
|
||||||
output(200, _('Modification done.'));
|
output(200, _('Modification done.'));
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<?php declare(strict_types=1); ?>
|
<?php declare(strict_types=1); ?>
|
||||||
<form method="post">
|
<form method="post">
|
||||||
<label for="zone"><?= _('Zone to be changed') ?></label>
|
<label for="domain"><?= _('Zone to be changed') ?></label>
|
||||||
<br>
|
<br>
|
||||||
<select required="" name="zone" id="zone">
|
<select required="" name="domain" id="domain">
|
||||||
<option value="" disabled="" selected="">-</option>
|
<option value="" disabled="" selected="">-</option>
|
||||||
<?php
|
<?php
|
||||||
foreach (nsListUserZones() as $zone)
|
foreach (nsListUserZones() as $domain)
|
||||||
echo ' <option value="' . $zone . '">' . $zone . '</option>' . LF;
|
echo ' <option value="' . $domain . '">' . $domain . '</option>' . LF;
|
||||||
?>
|
?>
|
||||||
</select>
|
</select>
|
||||||
<br>
|
<br>
|
||||||
|
@ -15,15 +15,15 @@ foreach (nsListUserZones() as $zone)
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
if (isset($data['zone_content'])) { // Display zone
|
if (isset($data['records'])) { // Display zone
|
||||||
|
|
||||||
?>
|
?>
|
||||||
<form method="post">
|
<form method="post">
|
||||||
<input type="hidden" name="zone" value="<?= $_POST['zone'] ?>">
|
<input type="hidden" name="domain" value="<?= $_POST['domain'] ?>">
|
||||||
|
|
||||||
<label for="zone-content"><?= sprintf(_('New content of the %s zone'), '<code><strong>' . $_POST['zone'] . '</strong></code>') ?></label>
|
<label for="zone-content"><?= sprintf(_('Authoritative records for %s'), '<code><strong>' . $_POST['domain'] . '</strong></code>') ?></label>
|
||||||
<br>
|
<br>
|
||||||
<textarea id="zone-content" name="zone-content" wrap="off" rows="<?= substr_count($data['zone_content'], LF) + 1 ?>"><?= htmlspecialchars($data['zone_content']) ?></textarea>
|
<textarea id="records" name="records" wrap="off" rows="<?= substr_count($data['records'], LF) + 1 ?>"><?= htmlspecialchars($data['records']) ?></textarea>
|
||||||
<br>
|
<br>
|
||||||
<input type="submit" value="<?= _('Replace') ?>">
|
<input type="submit" value="<?= _('Replace') ?>">
|
||||||
</form>
|
</form>
|
||||||
|
@ -38,21 +38,21 @@ displayFinalMessage($data);
|
||||||
|
|
||||||
<h2><?= _('Default values') ?></h2>
|
<h2><?= _('Default values') ?></h2>
|
||||||
|
|
||||||
<p><?= sprintf(_('If the TTL is omitted, it will default to %s seconds.'), '<code><time datetime="PT' . DEFAULT_TTL . 'S">' . DEFAULT_TTL . '</time></code>') ?></p>
|
<p><?= sprintf(_('If the TTL is omitted, it will default to %s seconds.'), '<code><time datetime="PT' . NS_DEFAULT_TTL . 'S">' . NS_DEFAULT_TTL . '</time></code>') ?></p>
|
||||||
|
|
||||||
<p><?= _('Precising the class (<code>IN</code>) is optional.') ?></p>
|
<p><?= _('Precising the class (<code>IN</code>) is optional.') ?></p>
|
||||||
|
|
||||||
<h2><?= _('Allowed values') ?></h2>
|
<h2><?= _('Allowed values') ?></h2>
|
||||||
|
|
||||||
<p><?= sprintf(_('Submitted zone content is limited to %s characters.'), ZONE_MAX_CHARACTERS) ?></p>
|
<p><?= sprintf(_('Submitted field content is limited to %s characters.'), NS_TEXTAREA_MAX_CHARACTERS) ?></p>
|
||||||
|
|
||||||
<p><?= sprintf(_('TTLs must last between %1$s and %2$s seconds.'), '<code><time datetime="PT' . MIN_TTL . 'S">' . MIN_TTL . '</time></code>', '<code><time datetime="PT' . MAX_TTL . 'S">' . MAX_TTL . '</time></code>') ?></p>
|
<p><?= sprintf(_('TTLs must last between %1$s and %2$s seconds.'), '<code><time datetime="PT' . NS_MIN_TTL . 'S">' . NS_MIN_TTL . '</time></code>', '<code><time datetime="PT' . NS_MAX_TTL . 'S">' . NS_MAX_TTL . '</time></code>') ?></p>
|
||||||
|
|
||||||
<p><?= _('The only types that can be defined are:') ?></p>
|
<p><?= _('The only types that can be defined here are:') ?></p>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<?php
|
<?php
|
||||||
foreach (ALLOWED_TYPES as $allowed_type)
|
foreach (NS_ALLOWED_TYPES as $allowed_type)
|
||||||
echo ' <li><code>' . $allowed_type . '</code></li>';
|
echo ' <li><code>' . $allowed_type . '</code></li>';
|
||||||
?>
|
?>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -32,7 +32,7 @@ foreach ($user_zones as $zone)
|
||||||
<div>
|
<div>
|
||||||
<label for="ttl-value"><?= _('Value') ?></label>
|
<label for="ttl-value"><?= _('Value') ?></label>
|
||||||
<br>
|
<br>
|
||||||
<input required="" id="ttl-value" list="ttls" name="ttl-value" size="6" type="number" min="1" max="432000" value="<?= $_POST['ttl-value'] ?? DEFAULT_TTL ?>" placeholder="<?= DEFAULT_TTL ?>">
|
<input required="" id="ttl-value" list="ttls" name="ttl-value" size="6" type="number" min="1" max="432000" value="<?= $_POST['ttl-value'] ?? NS_DEFAULT_TTL ?>" placeholder="<?= NS_DEFAULT_TTL ?>">
|
||||||
<datalist id="ttls">
|
<datalist id="ttls">
|
||||||
<option value="900">
|
<option value="900">
|
||||||
<option value="1800">
|
<option value="1800">
|
||||||
|
|
54
pg-view/reg/edit.php
Normal file
54
pg-view/reg/edit.php
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
<?php declare(strict_types=1); ?>
|
||||||
|
<form method="post">
|
||||||
|
<label for="domain"><?= _('Domain to be changed') ?></label>
|
||||||
|
<br>
|
||||||
|
<select required="" name="domain" id="domain">
|
||||||
|
<option value="" disabled="" selected="">-</option>
|
||||||
|
<?php
|
||||||
|
foreach (regListUserDomains() as $domain)
|
||||||
|
echo ' <option value="' . $domain . '">' . $domain . '</option>' . LF;
|
||||||
|
?>
|
||||||
|
</select>
|
||||||
|
<br>
|
||||||
|
<input type="submit" value="<?= _('Display') ?>">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
|
||||||
|
if (isset($data['records'])) { // Display zone
|
||||||
|
|
||||||
|
?>
|
||||||
|
<form method="post">
|
||||||
|
<input type="hidden" name="domain" value="<?= $_POST['domain'] ?>">
|
||||||
|
|
||||||
|
<label for="records"><?= sprintf(_('Delegation records for %s'), '<code><strong>' . $_POST['domain'] . '</strong></code>') ?></label>
|
||||||
|
<br>
|
||||||
|
<textarea id="records" name="records" wrap="off" rows="<?= substr_count($data['records'], LF) + 1 ?>"><?= htmlspecialchars($data['records']) ?></textarea>
|
||||||
|
<br>
|
||||||
|
<input type="submit" value="<?= _('Replace') ?>">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
displayFinalMessage($data);
|
||||||
|
|
||||||
|
?>
|
||||||
|
|
||||||
|
<h2><?= _('Input values') ?></h2>
|
||||||
|
|
||||||
|
<p><?= _('Precising the class (<code>IN</code>) is optional.') ?></p>
|
||||||
|
|
||||||
|
<p><?= sprintf(_('Submitted field content is limited to %s characters.'), REG_TEXTAREA_MAX_CHARACTERS) ?></p>
|
||||||
|
|
||||||
|
<p><?= sprintf(_('TTL values are ignored and always set to %s seconds.'), '<code><time datetime="PT' . CONF['reg']['ttl'] . 'S">' . CONF['reg']['ttl'] . '</time></code>') ?></p>
|
||||||
|
|
||||||
|
<p><?= _('The only types that can be defined here are:') ?></p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<?php
|
||||||
|
foreach (REG_ALLOWED_TYPES as $allowed_type)
|
||||||
|
echo ' <li><code>' . $allowed_type . '</code></li>';
|
||||||
|
?>
|
||||||
|
</ul>
|
Loading…
Reference in a new issue