diff --git a/db/migrations/006-create-ns-syncs-table.sql b/db/migrations/006-create-ns-syncs-table.sql new file mode 100644 index 0000000..baca87f --- /dev/null +++ b/db/migrations/006-create-ns-syncs-table.sql @@ -0,0 +1,12 @@ +BEGIN TRANSACTION; + +CREATE TABLE IF NOT EXISTS "ns-syncs" ( + "username" TEXT NOT NULL, + "source" TEXT NOT NULL, + "destination" TEXT NOT NULL UNIQUE, + UNIQUE("username", "source", "destination"), + FOREIGN KEY("username") REFERENCES "users"("id"), + FOREIGN KEY("destination") REFERENCES "zones"("zone") +); + +COMMIT; diff --git a/db/schema.sql b/db/schema.sql index 8233bbe..a0a664c 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -42,6 +42,14 @@ CREATE TABLE IF NOT EXISTS "zones" ( PRIMARY KEY("zone"), FOREIGN KEY("username") REFERENCES "users"("id") ); +CREATE TABLE IF NOT EXISTS "ns-syncs" ( + "username" TEXT NOT NULL, + "source" TEXT NOT NULL, + "destination" TEXT NOT NULL UNIQUE, + UNIQUE("username", "source", "destination"), + FOREIGN KEY("username") REFERENCES "users"("id"), + FOREIGN KEY("destination") REFERENCES "zones"("zone") +); CREATE TABLE IF NOT EXISTS "sites" ( "username" TEXT NOT NULL, "site_dir" TEXT NOT NULL, diff --git a/fn/auth.php b/fn/auth.php index fd3d48f..023bb41 100644 --- a/fn/auth.php +++ b/fn/auth.php @@ -98,9 +98,11 @@ function authDeleteUser(string $user_id): void { foreach (query('select', 'registry', ['username' => $user_id], 'domain') as $domain) regDeleteDomain($domain, $user_id); - if (in_array('ns', $user_services, true)) + if (in_array('ns', $user_services, true)) { + query('delete', 'ns-syncs', ['username' => $user_id]); foreach (query('select', 'zones', ['username' => $user_id], 'zone') as $zone) nsDeleteZone($zone, $user_id); + } if (in_array('ht', $user_services, true)) { foreach (query('select', 'sites', ['username' => $user_id]) as $site) diff --git a/fn/common.php b/fn/common.php index 33815e9..2ce54ca 100644 --- a/fn/common.php +++ b/fn/common.php @@ -21,6 +21,24 @@ function exescape(array $args, array &$output = NULL, int &$result_code = NULL): return $result_code; } +class KdigException extends Exception {}; +function kdig(string $name, string $type, string $server = NULL): array { + exescape([ + CONF['dns']['kdig_path'], + '+json', + '+timeout=5', + '+retry=0', + '-q', + $name, + '-t', + $type, + ...(isset($server) ? ['@' . $server] : []), + ], $output, $code); + if ($code !== 0) + throw new KdigException(); + return json_decode(implode(LF, $output), true, flags: JSON_THROW_ON_ERROR); +} + function insert(string $table, array $values): void { $query = 'INSERT INTO "' . $table . '"('; diff --git a/fn/dns.php b/fn/dns.php index 6c4b171..33597b6 100644 --- a/fn/dns.php +++ b/fn/dns.php @@ -1,16 +1,19 @@ !str_ends_with($elements[0], $filter_domain), + false => $elements[0] !== $filter_domain, + }) continue; // Ignore records for other domains if (!in_array($elements[2], $types, true)) continue; // Ignore records generated by Knot - array_push($parsed_zone_content, array_map('htmlspecialchars', $elements)); + array_push($parsed_zone_content, $elements); } return $parsed_zone_content; } diff --git a/fn/ns.php b/fn/ns.php index a659385..128fca1 100644 --- a/fn/ns.php +++ b/fn/ns.php @@ -17,6 +17,8 @@ const ALLOWED_TYPES = ['AAAA', 'A', 'TXT', 'SRV', 'MX', 'SVCB', 'HTTPS', 'NS', ' const ZONE_MAX_CHARACTERS = 10000; +const SYNC_TTL = 3600; + function nsParseCommonRequirements(): array { nsCheckZonePossession($_POST['zone']); diff --git a/init.php b/init.php index edc6694..7ac3acb 100644 --- a/init.php +++ b/init.php @@ -7,7 +7,7 @@ set_error_handler(function ($level, $message, $file = '', $line = 0) { set_exception_handler(function ($e) { error_log((string) $e); http_response_code(500); - echo '

Error

An error occured.'; + echo '

Error

An error occured.

'; }); register_shutdown_function(function () { // Also catch fatal errors if (($error = error_get_last()) !== NULL) diff --git a/jobs/ns-sync.php b/jobs/ns-sync.php new file mode 100644 index 0000000..934e32c --- /dev/null +++ b/jobs/ns-sync.php @@ -0,0 +1,42 @@ + _('Store geographic coordinates'), 'tokens_account_cost' => 120, ], + 'sync' => [ + 'title' => sprintf(_('Synchronized records')), + 'description' => _('Regularly fetch distant record value and update it to a local zone'), + 'tokens_account_cost' => 900, + ], ], 'ht' => [ 'index' => [ diff --git a/pg-act/ns/sync.php b/pg-act/ns/sync.php new file mode 100644 index 0000000..9275678 --- /dev/null +++ b/pg-act/ns/sync.php @@ -0,0 +1,37 @@ + 8) + output(403, 'Wrong elements number.'); + +foreach ($_POST['syncs'] as $i => &$sync) { + if (($sync['source'] ?? '') === '') { + unset($_POST['syncs'][$i]); + continue; + } + $sync['source'] = formatAbsoluteDomain($sync['source']); + nsCheckZonePossession($sync['destination']); +} +$syncs = array_values($_POST['syncs']); + +rateLimit(); + +try { + DB->beginTransaction(); + + query('delete', 'ns-syncs', ['username' => $_SESSION['id']]); + + foreach ($syncs as $sync) + insert('ns-syncs', [ + 'username' => $_SESSION['id'], + 'source' => $sync['source'], + 'destination' => $sync['destination'], + ]); + + DB->commit(); +} catch (Exception $e) { + DB->rollback(); + output(500, 'Database error.', [$e->getMessage()]); +} + +output(200, _('Synchronized records updated.')); diff --git a/pg-view/ht/keys.php b/pg-view/ht/keys.php index e99f28c..3f613ef 100755 --- a/pg-view/ht/keys.php +++ b/pg-view/ht/keys.php @@ -16,12 +16,12 @@ foreach (array_slice(array_merge(query('select', 'ssh-keys', ['username' => $_SE

-
- ssh-ed15519 +
+ ssh-ed15519
-
- +
+

+ +
+ +
+ +
+ + + +