'; foreach($headers as $header) { $header = (object)$header; $html .= '<' . $header->tag; if(isset($header->props)) $html .= rgDnsConstructProps($header->props); $html .= empty($header->close) ? ' />' : '>tag . '>'; } $html .= '

Railgun DNS

'; if(rgDnsLoggedIn()) { $html .= 'Main Menu | '; $html .= 'Logged in as ' . $GLOBALS['userInfo']->user_name . ' [ '; $html .= 'Log out ]'; } else $html .= 'Log in'; $html .= '
'; echo $html; } function rgDnsFoot(): void { $html = '
'; echo $html; } function rgDnsConstructProps(array $props): string { $html = ''; foreach($props as $name => $value) $html .= ' ' . $name . (empty($value) ? '' : '="' . htmlspecialchars($value) . '"'); return $html; } function rgDnsForm(string $action, array $fields, array $buttons = [], string $method = 'post', string $encType = 'application/x-www-form-urlencoded'): void { $html = '
'; foreach($fields as $name => $info) if(empty($info['hidden'])) $html .= rgDnsFormField($name, $info); $html .= '
'; if(empty($buttons)) $buttons = ['Submit']; foreach($buttons as $button) { if(is_string($button)) $button = ['text' => $button]; $html .= ''; } echo $html . '
'; } function rgDnsFormField(string $name, array $fi): string { $fi = (object)$fi; // objects are less annoying to address switch($fi->type = ($fi->type ?? 'text')) { case 'hidden': return ''; case 'checkbox': return ''; case 'select': $html = ''; default: return ''; break; } } function rgDnsHttpInfo(string $title, array $lines = [], array $headers = []): void { rgDnsHead($title, $headers); echo '

' . $title . '

'; foreach($lines as $line) echo '

' . $line . '

'; echo '
'; rgDnsFoot(); } function rgDnsHttpError(int $code): void { http_response_code($code); rgDnsHttpInfo('Error #' . $code, [ 'Click here to return to where you came from.', ]); exit; } function rgDnsGet(string $name): mixed { return $_SESSION[RGDNS_PREFIX . $name] ?? null; } function rgDnsSet(string $name, $value): mixed { return $_SESSION[RGDNS_PREFIX . $name] = $value; } function rgDnsIsset(string $name): bool { return isset($_SESSION[RGDNS_PREFIX . $name]); } function rgDnsLoggedIn(): bool { return rgDnsIsset('uid'); } function rgDnsConfig(string $name, $value = null) { if($value === null) return $GLOBALS[RGDNS_PREFIX . 'config'][$name] ?? null; $GLOBALS[RGDNS_PREFIX . 'config'][$name] = $value; } function rgDnsBase32Decode(string $str, string $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'): string { $out = ''; $length = strlen($str); $char = $shift = 0; for($i = 0; $i < $length; $i++) { $char <<= 5; $char += stripos($chars, $str[$i]); $shift = ($shift + 5) % 8; $out .= $shift < 5 ? chr(($char & (0xFF << $shift)) >> $shift) : ''; } return $out; } function rgDnsBase32Encode(string $data, string $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'): string { $bin = ''; $encoded = ''; $length = strlen($data); for($i = 0; $i < $length; $i++) $bin .= sprintf('%08b', ord($data[$i])); $bin = str_split($bin, 5); $last = array_pop($bin); $bin[] = str_pad($last, 5, '0', STR_PAD_RIGHT); foreach($bin as $part) $encoded .= $chars[bindec($part)]; return $encoded; } function rgDnsTOTPTime(?int $time = null, int $interval = 30): int { $time ??= time(); return ($time * 1000) / ($interval * 1000); } function rgDnsTOTPGenerateSecret(): string { return rgDnsBase32Encode(random_bytes(16)); } function rgDnsTOTPGenerate(string $privateKey, ?int $time = null, int $digits = 6, int $interval = 30, string $algo = 'sha1'): string { $hash = hash_hmac($algo, pack('J', rgDnsTOTPTime($time)), rgDnsBase32Decode($privateKey), true); $offset = ord($hash[strlen($hash) - 1]) & 0x0F; $bin = 0; $bin |= (ord($hash[$offset]) & 0x7F) << 24; $bin |= (ord($hash[$offset + 1]) & 0xFF) << 16; $bin |= (ord($hash[$offset + 2]) & 0xFF) << 8; $bin |= (ord($hash[$offset + 3]) & 0xFF); $otp = $bin % pow(10, $digits); return str_pad($otp, $digits, STR_PAD_LEFT); } function rgDnsTOTPValidTokens(string $privateKey): array { return [ rgDnsTOTPGenerate($privateKey, time()), rgDnsTOTPGenerate($privateKey, time() - 30), rgDnsTOTPGenerate($privateKey, time() + 30) ]; } function rgDnsCan(int $perm, ?stdClass $userInfo = null): bool { $userInfo ??= $GLOBALS['userInfo']; return $userInfo !== null && ($userInfo->user_perms & $perm) === $perm; } function rgDnsDb(): PDO { $name = RGDNS_PREFIX . 'pdo'; if(empty($GLOBALS[$name])) try { $GLOBALS[$name] = new PDO( rgDnsConfig('pdo_dsn'), rgDnsConfig('pdo_user'), rgDnsConfig('pdo_pass'), [ PDO::ATTR_CASE => PDO::CASE_NATURAL, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ, PDO::ATTR_STRINGIFY_FETCHES => false, PDO::ATTR_EMULATE_PREPARES => false, PDO::MYSQL_ATTR_INIT_COMMAND => 'SET SESSION time_zone = \'+00:00\'' . ', sql_mode = \'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION\'', ]); } catch(PDOException $ex) { die($ex->getMessage()); } return $GLOBALS[$name]; } function rgDnsCanLogin(?string $ipAddress = null): bool { $ipAddress ??= $_SERVER['REMOTE_ADDR']; $prep = rgDnsDb()->prepare('SELECT COUNT(*) < 5 FROM `' . RGDNS_PREFIX . 'attempts` WHERE `attempt_ip` = INET6_ATON(:ip) AND `attempt_success` = 0'); $prep->bindValue('ip', $ipAddress); return $prep->execute() && (bool)$prep->fetchColumn(); } function rgDnsRecordLogIn(bool $success, ?string $ipAddress = null): void { $ipAddress ??= $_SERVER['REMOTE_ADDR']; $prep = rgDnsDb()->prepare('INSERT INTO `' . RGDNS_PREFIX . 'attempts` (`attempt_ip`, `attempt_success`) VALUES (INET6_ATON(:ip), :success)'); $prep->bindValue('ip', $ipAddress); $prep->bindValue('success', $success ? 1 : 0); $prep->execute(); } function rgDnsCleanLogInAttempts(): void { rgDnsDb()->exec('DELETE FROM `' . RGDNS_PREFIX . 'attempts` WHERE `attempt_created` < NOW() - INTERVAL 1 HOUR'); } function rgDnsUniqueChars(string $input, bool $multibyte = true): int { $chars = []; $strlen = $multibyte ? 'mb_strlen' : 'strlen'; $substr = $multibyte ? 'mb_substr' : 'substr'; $length = $strlen($input); for($i = 0; $i < $length; $i++) { $current = $substr($input, $i, 1); if(!in_array($current, $chars, true)) { $chars[] = $current; } } return count($chars); } function rgDnsCheckPasswordStrength(string $password): bool { return rgDnsUniqueChars($password) >= 6; } function rgDnsFetchUser(string $field, $match): stdClass|bool { $prep = rgDnsDb()->prepare('SELECT *, UNIX_TIMESTAMP(`user_created`) AS `user_created`, UNIX_TIMESTAMP(`user_disabled`) AS `user_disabled` FROM `' . RGDNS_PREFIX . 'users` WHERE `user_' . $field . '` = :match'); $prep->bindValue('match', $match); $prep->execute(); return $prep->fetchObject(); } function rgDnsFetchUsers(string $field, $after, int $limit = 10): array { $prep = rgDnsDb()->prepare('SELECT *, UNIX_TIMESTAMP(`user_created`) AS `user_created`, UNIX_TIMESTAMP(`user_disabled`) AS `user_disabled` FROM `' . RGDNS_PREFIX . 'users` WHERE `user_' . $field . '` > :after LIMIT :limit'); $prep->bindValue('after', $after); $prep->bindValue('limit', $limit); $prep->execute(); $items = []; while($item = $prep->fetchObject()) $items[] = $item; return $items; } function rgDnsSetUserPerms(int $userId, int $perms): void { $prep = rgDnsDb()->prepare('UPDATE `' . RGDNS_PREFIX . 'users` SET `user_perms` = :perms WHERE `user_id` = :user'); $prep->bindValue('user', $userId); $prep->bindValue('perms', $perms); $prep->execute(); } function rgDnsSetUserTOTPSecret(int $userId, ?string $privateKey): void { $prep = rgDnsDb()->prepare('UPDATE `' . RGDNS_PREFIX . 'users` SET `user_totp_private` = :priv WHERE `user_id` = :user'); $prep->bindValue('user', $userId); $prep->bindValue('priv', $privateKey); $prep->execute(); } function rgDnsSetUserPassword(int $userId, string $password): void { $prep = rgDnsDb()->prepare('UPDATE `' . RGDNS_PREFIX . 'users` SET `user_password` = :pwd WHERE `user_id` = :user'); $prep->bindValue('user', $userId); $prep->bindValue('pwd', $password); $prep->execute(); } function rgDnsQueryLoginAttempts(int $start): array { $prep = rgDnsDb()->prepare('SELECT *, INET6_NTOA(`attempt_ip`) AS `attempt_ip`, UNIX_TIMESTAMP(`attempt_created`) AS `attempt_created` FROM `' . RGDNS_PREFIX . 'attempts` WHERE `attempt_created` > FROM_UNIXTIME(:start) ORDER BY `attempt_created` ASC'); $prep->bindValue('start', $start); $prep->execute(); $items = []; while($item = $prep->fetchObject()) $items[] = $item; return $items; } function rgDnsFetchNameserver(string $field, $match): stdClass|bool { $prep = rgDnsDb()->prepare('SELECT *, UNIX_TIMESTAMP(`ns_created`) AS `user_created`, UNIX_TIMESTAMP(`ns_disabled`) AS `user_disabled`, u.`user_name` AS `ns_owner_name` FROM `' . RGDNS_PREFIX . 'nameservers` AS ns LEFT JOIN `' . RGDNS_PREFIX . 'users` AS u ON ns.`ns_owner` = u.`user_id` WHERE `ns_' . $field . '` = :match'); $prep->bindValue('match', $match); $prep->execute(); return $prep->fetchObject(); } function rgDnsFetchNameservers(bool $withDisabled = true): array { $prep = rgDnsDb()->prepare('SELECT ns.*, UNIX_TIMESTAMP(ns.`ns_created`) AS `ns_created`, UNIX_TIMESTAMP(ns.`ns_disabled`) AS `ns_disabled`, u.`user_name` AS `ns_owner_name` FROM `' . RGDNS_PREFIX . 'nameservers` AS ns LEFT JOIN `' . RGDNS_PREFIX . 'users` AS u ON ns.`ns_owner` = u.`user_id` ' . ($withDisabled ? '' : ' WHERE `ns_disabled` IS NULL')); $prep->execute(); $items = []; while($item = $prep->fetchObject()) $items[] = $item; return $items; } function rgDnsDeleteNameserver(int $nsId): void { $prep = rgDnsDb()->prepare('DELETE FROM `' . RGDNS_PREFIX . 'nameservers` WHERE `ns_id` = :id'); $prep->bindValue('id', $nsId); $prep->execute(); } function rgDnsEditNameserver(int $nsId, string $name, bool $master, bool $disable): bool { $prep = rgDnsDb()->prepare('UPDATE `' . RGDNS_PREFIX . 'nameservers` SET `ns_name` = :name, `ns_type` = :type, `ns_disabled` = IF(:disable, COALESCE(`ns_disabled`, NOW()), NULL) WHERE `ns_id` = :id'); $prep->bindValue('id', $nsId); $prep->bindValue('name', $name); $prep->bindValue('type', $master ? 'MASTER' : 'SLAVE'); $prep->bindValue('disable', $disable ? 1 : 0); try { $prep->execute(); } catch(PDOException $ex) { if($ex->getCode() == '23000') return false; throw $ex; } return true; } function rgDnsAddNameserver(string $name, bool $master, bool $disable, int $owner): int { $prep = rgDnsDb()->prepare('INSERT INTO `' . RGDNS_PREFIX . 'nameservers` (`ns_name`, `ns_owner`, `ns_type`, `ns_disabled`) VALUES (:name, :owner, :type, IF(:disable, NOW(), NULL))'); $prep->bindValue('name', $name); $prep->bindValue('owner', $owner); $prep->bindValue('type', $master ? 'MASTER' : 'SLAVE'); $prep->bindValue('disable', $disable ? 1 : 0); try { $prep->execute(); } catch(PDOException $ex) { if($ex->getCode() == '23000') return -1; throw $ex; } return (int)rgDnsDb()->lastInsertId(); } function rgDnsFetchNameserverAddresses(int $nsId): array { $prep = rgDnsDb()->prepare('SELECT *, INET6_NTOA(`server_ip`) AS `server_ip` FROM `' . RGDNS_PREFIX . 'nameservers_addresses` WHERE `server_ns` = :ns'); $prep->bindValue('ns', $nsId); $prep->execute(); $items = []; while($item = $prep->fetchObject()) $items[] = $item; return $items; } function rgDnsAddNameserverAddress(int $nsId, string $address): bool { $prep = rgDnsDb()->prepare('INSERT INTO `' . RGDNS_PREFIX . 'nameservers_addresses` (`server_ns`, `server_ip`) VALUES (:ns, INET6_ATON(:addr))'); $prep->bindValue('ns', $nsId); $prep->bindValue('addr', $address); try { $prep->execute(); } catch(PDOException $ex) { if($ex->getCode() == '23000') return false; throw $ex; } return true; } function rgDnsRemoveNameserverAddress(int $nsId, string $address): void { $prep = rgDnsDb()->prepare('DELETE FROM `' . RGDNS_PREFIX . 'nameservers_addresses` WHERE `server_ns` = :ns AND `server_ip` = INET6_ATON(:addr)'); $prep->bindValue('ns', $nsId); $prep->bindValue('addr', $address); $prep->execute(); } function rgDnsFetchDomain(string $field, $match): stdClass|false { $prep = rgDnsDb()->prepare('SELECT d.*, UNIX_TIMESTAMP(d.`domain_created`) AS `domain_created`, UNIX_TIMESTAMP(d.`domain_disabled`) AS `domain_disabled`, u.`user_name` AS `domain_owner_name` FROM `' . RGDNS_PREFIX . 'domains` AS d LEFT JOIN `' . RGDNS_PREFIX . 'users` AS u ON d.`domain_owner` = u.`user_id` WHERE d.`domain_' . $field . '` = :match'); $prep->bindValue('match', $match); $prep->execute(); return $prep->fetchObject(); } function rgDnsFetchDomains(?int $owner = null, ?int $after = null, int $limit = 10): array { $hasOwner = $owner !== null; $hasAfter = $after !== null; $query = 'SELECT d.*, UNIX_TIMESTAMP(d.`domain_created`) AS `domain_created`, UNIX_TIMESTAMP(d.`domain_disabled`) AS `domain_disabled`, u.`user_name` AS `domain_owner_name` FROM `' . RGDNS_PREFIX . 'domains` AS d LEFT JOIN `' . RGDNS_PREFIX . 'users` AS u ON d.`domain_owner` = u.`user_id`'; if($hasOwner || $hasAfter) { $query .= ' WHERE'; if($hasOwner) { $query .= ' d.`domain_owner` = :owner'; if($hasAfter) $query .= ' AND'; } if($hasAfter) $query .= ' d.`domain_id` > :after LIMIT :limit'; } $prep = rgDnsDb()->prepare($query); if($hasOwner) $prep->bindValue('owner', $owner); if($hasAfter) { $prep->bindValue('after', $after); $prep->bindValue('limit', $limit); } $prep->execute(); $items = []; while($item = $prep->fetchObject()) $items[] = $item; return $items; } function rgDnsDeleteDomain(int $domainId): void { $prep = rgDnsDb()->prepare('DELETE FROM `' . RGDNS_PREFIX . 'domains` WHERE `domain_id` = :domain'); $prep->bindValue('domain', $domainId); $prep->execute(); } function rgDnsAddDomain(int $userId, string $domainName, int $domainTTL, ?int $domainRefresh, ?int $domainRetry, ?int $domainExpire, ?int $domainCacheTTL, ?bool $domainDisable): int { $prep = rgDnsDb()->prepare('INSERT INTO `' . RGDNS_PREFIX . 'domains` (`domain_name`, `domain_owner`, `domain_refresh`, `domain_retry`, `domain_expire`, `domain_ttl`, `domain_record_ttl`, `domain_disabled`) VALUES (:name, :owner, COALESCE(:refresh, DEFAULT(`domain_refresh`)), COALESCE(:retry, DEFAULT(`domain_retry`)), COALESCE(:expire, DEFAULT(`domain_expire`)), COALESCE(:cttl, DEFAULT(`domain_ttl`)), :rttl, IF(:disable, NOW(), NULL))'); $prep->bindValue('name', $domainName); $prep->bindValue('owner', $userId); $prep->bindValue('refresh', $domainRefresh); $prep->bindValue('retry', $domainRetry); $prep->bindValue('expire', $domainExpire); $prep->bindValue('cttl', $domainCacheTTL); $prep->bindValue('rttl', $domainTTL); $prep->bindValue('disable', $domainDisable ? 1 : 0); try { $prep->execute(); } catch(PDOException $ex) { var_dump($ex->getMessage()); if($ex->getCode() == '23000') return -1; throw $ex; } return (int)rgDnsDb()->lastInsertId(); } function rgDnsEditDomain(int $domainId, int $domainTTL, ?int $domainRefresh, ?int $domainRetry, ?int $domainExpire, ?int $domainCacheTTL, ?bool $domainDisable): bool { $prep = rgDnsDb()->prepare('UPDATE `' . RGDNS_PREFIX . 'domains` SET `domain_record_ttl` = :rttl, `domain_refresh` = COALESCE(:refresh, `domain_refresh`), `domain_retry` = COALESCE(:retry, `domain_retry`), `domain_expire` = COALESCE(:expire, `domain_expire`), `domain_ttl` = COALESCE(:cttl, `domain_ttl`), `domain_disabled` = IF(:hasDisable, IF(:disable, COALESCE(`domain_disabled`, NOW()), NULL), `domain_disabled`) WHERE `domain_id` = :domain'); $prep->bindValue('domain', $domainId); $prep->bindValue('refresh', $domainRefresh); $prep->bindValue('retry', $domainRetry); $prep->bindValue('expire', $domainExpire); $prep->bindValue('cttl', $domainCacheTTL); $prep->bindValue('rttl', $domainTTL); $prep->bindValue('hasDisable', ($domainDisable === null) ? 0 : 1); $prep->bindValue('disable', $domainDisable ? 1 : 0); try { $prep->execute(); } catch(PDOException $ex) { if($ex->getCode() == '23000') return false; throw $ex; } return true; } function rgDnsFetchRecord(string $field, $match): stdClass|bool { $prep = rgDnsDb()->prepare('SELECT *, UNIX_TIMESTAMP(`record_created`) AS `record_created`, UNIX_TIMESTAMP(`record_disabled`) AS `record_disabled` FROM `' . RGDNS_PREFIX . 'records` WHERE `record_' . $field . '` = :match'); $prep->bindValue('match', $match); $prep->execute(); return $prep->fetchObject(); } function rgDnsFetchRecords(int $domainId): array { $prep = rgDnsDb()->prepare('SELECT *, UNIX_TIMESTAMP(`record_created`) AS `record_created`, UNIX_TIMESTAMP(`record_disabled`) AS `record_disabled` FROM `' . RGDNS_PREFIX . 'records` WHERE `record_domain` = :domain ORDER BY `record_name`, `record_type`, `record_priority`'); $prep->bindValue('domain', $domainId); $prep->execute(); $items = []; while($item = $prep->fetchObject()) $items[] = $item; return $items; } function rgDnsDeleteRecord(int $recordId): void { $prep = rgDnsDb()->prepare('DELETE FROM `' . RGDNS_PREFIX . 'records` WHERE `record_id` = :record'); $prep->bindValue('record', $recordId); $prep->execute(); } function rgDnsFormatRecordValue(string $type, string $value): string { switch($type) { case 'TXT': if(strlen($value) > 100) $value = substr($value, 0, 100) . '...'; $value = '"' . $value . '"'; } return htmlspecialchars($value); } $GLOBALS[RGDNS_PREFIX . 'config'] = []; require_once __DIR__ . '/config.php'; rgDnsCleanLogInAttempts(); session_name(RGDNS_PREFIX . 'session'); ini_set('session.gc_maxlifetime', 86400); session_set_cookie_params(86400); session_start(); if(!rgDnsLoggedIn()) { $canLogin = rgDnsCanLogin(); if(!$canLogin) { $error = 'You may no longer log in.'; } elseif(!empty($_POST)) { $sessionId = filter_input(INPUT_POST, 'sessid'); if($sessionId !== session_id()) { rgDnsRecordLogIn(false); $error = 'Username, password or token was invalid.'; } else { $userName = filter_input(INPUT_POST, 'username'); $password = filter_input(INPUT_POST, 'password'); if(empty($userName) || empty($password)) { rgDnsRecordLogIn(false); $error = 'Username, password or token was invalid.'; } else { $userInfo = rgDnsFetchUser('name', $userName); if(!$userInfo || !empty($userInfo->user_disabled) || !password_verify($password, $userInfo->user_password)) { rgDnsRecordLogIn(false); $error = 'Username, password or token was invalid.'; } else { $mfaDisarmed = empty($userInfo->user_totp_private); if(!$mfaDisarmed) { $mfaToken = filter_input(INPUT_POST, 'mfa'); $mfaValid = rgDnsTOTPValidTokens($userInfo->user_totp_private); $mfaDisarmed = in_array($mfaToken, $mfaValid, true); } if(!$mfaDisarmed) { rgDnsRecordLogIn(false); $error = 'Username, password or token was invalid.'; } else { rgDnsRecordLogIn(true); rgDnsSet('uid', $userInfo->user_id); rgDnsHead('Logged in!', [ ['tag' => 'meta', 'props' => ['http-equiv' => 'refresh', 'content' => '2; url=?']], ]); echo 'Logged in successfully! You will now be redirected to the main page.'; rgDnsFoot(); exit; } } } } } rgDnsHead('Login'); echo '

Log in

'; if(isset($error)) echo '
' . $error . '
'; rgDnsForm('?', [ 'sessid' => [ 'type' => 'hidden', 'value' => $canLogin ? session_id() : '', ], 'username' => [ 'title' => 'Username', 'disable' => !$canLogin, ], 'password' => [ 'title' => 'Password', 'type' => 'password', 'disable' => !$canLogin, ], 'mfa' => [ 'title' => 'Authenticator Token', 'type' => 'text', 'disable' => !$canLogin, 'monospace' => true, 'props' => [ 'maxlength' => 6, 'placeholder' => '------', 'style' => 'text-align:center', 'autocomplete' => 'off', ], ], ], [[ 'text' => 'Log in', 'disable' => !$canLogin, ]]); echo '
'; rgDnsFoot(); exit; } $userInfo = rgDnsFetchUser('id', rgDnsGet('uid')); if(empty($userInfo) || !empty($userInfo->user_disabled)) { session_destroy(); rgDnsHttpInfo('Account Disabled', [ 'Your account has been disabled. You will now be redirected to the login page.', ], [ ['tag' => 'meta', 'props' => ['http-equiv' => 'refresh', 'content' => '5; url=?']], ]); exit; } switch(filter_input(INPUT_GET, 'mode')) { case '': rgDnsHead('Main Menu'); $showLine = false; echo '

Main Menu

'; rgDnsFoot(); break; case 'records': if(!rgDnsCan(RGDNSP_MANAGE_RECORDS)) rgDnsHttpError(403); $returnUrl = (string)filter_input(INPUT_GET, 'ret'); if(substr($returnUrl, 0, 6) !== '?mode=') $returnUrl = '?mode=domains'; $returnUrl = htmlspecialchars($returnUrl); $manageAllDomains = rgDnsCan(RGDNSP_MANAGE_ALL_DOMAINS); // read-only $manageAllRecords = rgDnsCan(RGDNSP_MANAGE_ALL_RECORDS); // read-write $manageAddRecords = rgDnsCan(RGDNSP_ADD_RECORDS); // can write at all $domainId = (int)filter_input(INPUT_GET, 'di'); $domainInfo = rgDnsFetchDomain('id', $domainId); if(empty($domainInfo)) { rgDnsHttpError(404); break; } if(!$manageAllDomains && $domainInfo->domain_owner !== $userInfo->user_id) { rgDnsHttpError(403); break; } if(isset($_GET['smode'])) { $recordId = (int)filter_input(INPUT_GET, 'ri', FILTER_SANITIZE_NUMBER_INT); $hasRecord = $recordId > 0; if($hasRecord) { $recordInfo = rgDnsFetchRecord('id', $recordId); if($recordInfo->record_domain !== $domainInfo->domain_id) { rgDnsHttpError(403); break; } } else $recordInfo = null; $returnNoAmp = '?mode=records&di=' . $domainInfo->domain_id . '&ret=' . rawurlencode($returnUrl); $return = htmlspecialchars($returnNoAmp); switch(filter_input(INPUT_GET, 'smode')) { case 'add': case 'edit': if(!$hasRecord && !$manageAddRecords) { rgDnsHttpError(403); break; } if(!empty($_POST)) { if(filter_input(INPUT_POST, 'sessid') !== session_id()) { $error = 'Invalid request.'; } else { $recordTTL = ($recordTTL = (int)(filter_input(INPUT_POST, 'rttl'))) < 1 ? null : $recordTTL; $recordPrio = ($recordPrio = (int)(filter_input(INPUT_POST, 'rprio'))) < 1 ? 10 : $recordPrio; $recordValue = (string)filter_input(INPUT_POST, 'rvalue'); $recordDisable = !empty($_POST['rdisable']); if($domainIsset) { if($recordInfo->record_type !== 'MX') $recordPrio = null; if(rgDnsEditDomain($recordInfo->record_id, $recordTTL, $recordPrio, $recordValue, $recordDisable)) { $recordInfo->record_ttl = $recordTTL; $recordInfo->record_priority = $recordPrio; $recordInfo->record_value = $recordValue; $recordInfo->record_disabled = $recordDisable ? ($recordInfo->record_disabled ?? time()) : null; } else $error = 'This domain has already been added to the system.'; } else { $recordName = trim((string)filter_input(INPUT_POST, 'rname'), " \n\r\t\v\0."); $recordType = (string)filter_input(INPUT_POST, 'rtype'); $recordId = rgDnsAddRecord($domainInfo->domain_id, $recordName, $recordType, $recordTTL, $recordPrio, $recordValue, $recordDisable); if($recordId < 1) $error = 'Something went wrong while adding that record.'; else { $recordInfo = rgDnsFetchRecord('id', $recordId); $hasRecord = true; } } } } $title = $hasRecord ? ('Editing ' . $recordInfo->record_type . ' record ' . $recordInfo->record_name . ' for ' . $domainInfo->domain_name) : ('Adding a new DNS record for ' . $domainInfo->domain_name); $formTarget = $return . '&smode=' . ($hasRecord ? ('edit&ri=' . $recordInfo->record_id) : 'add'); rgDnsHead($title); echo '

' . $title . '

'; if(isset($error)) echo '

Error: ' . $error . '

'; echo ''; echo '
'; $recordFields = [ 'sessid' => ['type' => 'hidden', 'value' => session_id()], 'rname' => [ 'type' => 'text', 'title' => 'Name', 'value' => ($recordInfo?->record_name ?? ''), 'disable' => $hasRecord, ], 'rtype' => [ 'type' => 'select', 'title' => 'Type', 'value' => ($recordInfo?->record_type ?? ''), 'options' => RGDNS_RECORD_TYPES, 'disable' => $hasRecord, ], 'rttl' => [ 'type' => 'number', 'title' => 'TTL (in Seconds)', 'value' => ($recordInfo?->record_ttl ?? ''), 'props' => ['placeholder' => $domainInfo->domain_record_ttl], ], 'rprio' => [ 'type' => 'number', 'title' => 'Priority (for MX)', 'value' => ($recordInfo?->record_priority ?? ''), 'hidden' => $hasRecord && $recordInfo->record_type !== 'MX', 'props' => ['placeholder' => '10'], ], 'rvalue' => [ 'type' => 'text', 'title' => 'Value', 'value' => ($recordInfo?->record_value ?? ''), ], 'rdisable' => [ 'type' => 'checkbox', 'title' => 'Disabled', 'value' => !empty($recordInfo->record_disabled), ], ]; rgDnsForm($formTarget, $recordFields); echo '
'; echo '
'; rgDnsFoot(); break; case 'delete': if(filter_input(INPUT_GET, 'confirm') === session_id()) { rgDnsDeleteRecord($recordInfo->record_id); rgDnsHttpInfo('Successfully deleted ' . $recordInfo->record_type . ' record ' . $recordInfo->record_name . ' from ' . $domainInfo->domain_name . '!', [ 'The record has been deleted successfully. You will now be redirected to the records page.', ], [ ['tag' => 'meta', 'props' => ['http-equiv' => 'refresh', 'content' => '2; url=' . $returnNoAmp]], ]); break; } $title = 'Deleting ' . $recordInfo->record_type . ' record ' . $recordInfo->record_name . ' from ' . $domainInfo->domain_name; rgDnsHead($title); echo '

' . $title . '

' . '

Are you sure you want to irreversably delete this record?

' . '
'; rgDnsFoot(); break; default: rgDnsHttpError(404); break; } } else { $records = rgDnsFetchRecords($domainInfo->domain_id); $title = 'DNS Records for ' . $domainInfo->domain_name; rgDnsHead($title); echo '

' . $title . '

'; if(!empty($returnUrl)) echo ''; if($manageAddRecords) echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; foreach($records as $record) { echo ''; printf( '', strtolower($record->record_type), $record->record_type, $record->record_name, empty($record->record_ttl) ? 'rgdns-records-default' : '', $record->record_ttl ?? $domainInfo->domain_record_ttl, $record->record_priority ?? '', rgDnsFormatRecordValue($record->record_type, $record->record_value), ); echo ''; } echo ''; echo '
TypeNameTTL (Seconds)PriorityValue
%s%s%d%s%s'; echo 'Edit'; echo ' Delete'; echo '
'; echo '
'; rgDnsFoot(); } break; case 'domains': if(!rgDnsCan(RGDNSP_MANAGE_DOMAINS)) rgDnsHttpError(403); $manageAll = rgDnsCan(RGDNSP_MANAGE_ALL_DOMAINS); $domainsUserId = $manageAll ? filter_input(INPUT_GET, 'uid', FILTER_SANITIZE_NUMBER_INT) : $userInfo->user_id; if(isset($_GET['smode'])) { $domainsUserId ??= $userInfo->user_id; $domainIsset = isset($_GET['di']); $domain = $domainIsset ? rgDnsFetchDomain('id', (int)filter_input(INPUT_GET, 'di', FILTER_SANITIZE_NUMBER_INT)) : null; $returnPrefix = '?mode=domains'; $return = (string)filter_input(INPUT_GET, 'ret'); if(substr($return, 0, strlen($returnPrefix)) !== $returnPrefix) $return = $returnPrefix; $returnNoAmp = rawurlencode($return); $return = htmlspecialchars($return); if($domainIsset) { if(empty($domain)) { rgDnsHttpError(404); break; } if(!$manageAll && ($domain->domain_owner !== $userInfo->user_id || !empty($domain->domain_disabled))) { rgDnsHttpError(403); break; } } $domainsUserInfo = $domainsUserId === $userInfo->user_id ? $userInfo : rgDnsFetchUser('id', $domainsUserId); if(empty($domainsUserInfo)) { rgDnsHttpError(400); break; } switch(filter_input(INPUT_GET, 'smode')) { case 'add': case 'edit': if(!rgDnsCan(RGDNSP_ADD_DOMAINS)) { rgDnsHttpError(403); break; } if(!empty($_POST)) { if(filter_input(INPUT_POST, 'sessid') !== session_id()) { $error = 'Invalid request.'; } else { $domainTTL = ($domainTTL = (int)(filter_input(INPUT_POST, 'drttl'))) < 1 ? 3600 : $domainTTL; $domainRefresh = $manageAll ? (int)(filter_input(INPUT_POST, 'drefresh') ?? 3600) : null; $domainRetry = $manageAll ? (int)(filter_input(INPUT_POST, 'dretry') ?? 1800) : null; $domainExpire = $manageAll ? (int)(filter_input(INPUT_POST, 'dexpire') ?? 1209600) : null; $domainCacheTTL = $manageAll ? (int)(filter_input(INPUT_POST, 'dcttl')) : null; $domainDisable = $manageAll ? !empty($_POST['ddisable']) : null; if($domainIsset) { if(rgDnsEditDomain($domain->domain_id, $domainTTL, $domainRefresh, $domainRetry, $domainExpire, $domainCacheTTL, $domainDisable)) { $domain->domain_record_ttl = $domainTTL; $domain->domain_refresh = $domainRefresh ?? $domain->domain_refresh; $domain->domain_retry = $domainRetry ?? $domain->domain_retry; $domain->domain_expire = $domainExpire ?? $domain->domain_expire; $domain->domain_ttl = $domainCacheTTL ?? $domain->domain_ttl; $domain->domain_disabled = $domainDisable ? ($domain->domain_disabled ?? time()) : null; } else $error = 'This domain has already been added to the system.'; } else { $domainName = trim((string)filter_input(INPUT_POST, 'dname'), " \n\r\t\v\0."); $domainId = rgDnsAddDomain($domainsUserInfo->user_id, $domainName, $domainTTL, $domainRefresh, $domainRetry, $domainExpire, $domainCacheTTL, $domainDisable); if($domainId < 1) $error = 'This domain has already been added to the system.'; else { $domain = rgDnsFetchDomain('id', $domainId); $domainIsset = true; } } } } $title = $domainIsset ? ('Editing domain #' . $domain->domain_id . ' ' . $domain->domain_name) : 'Adding a new Domain'; $formTarget = $return . '&smode=' . ($domainIsset ? ('edit&di=' . $domain->domain_id) : 'add') . '&ret=' . rawurlencode($return); rgDnsHead($title); echo '

' . $title . '

'; if(isset($error)) echo '

Error: ' . $error . '

'; echo ''; echo '
'; $domainFields = [ 'sessid' => ['type' => 'hidden', 'value' => session_id()], 'dname' => [ 'type' => 'text', 'title' => 'Name', 'value' => ($domain?->domain_name ?? ''), 'disable' => $domainIsset, ], 'drttl' => [ 'type' => 'number', 'title' => 'Default TTL (Seconds)', 'value' => ($domain?->domain_record_ttl ?? ''), 'props' => ['placeholder' => '3600'], ], ]; if($manageAll) { $domainFields['drefresh'] = [ 'type' => 'number', 'title' => 'Refresh (Seconds)', 'value' => ($domain?->domain_refresh ?? ''), 'props' => ['placeholder' => '3600'], ]; $domainFields['dretry'] = [ 'type' => 'number', 'title' => 'Retry (Seconds)', 'value' => ($domain?->domain_retry ?? ''), 'props' => ['placeholder' => '1800'], ]; $domainFields['dexpire'] = [ 'type' => 'number', 'title' => 'Expires (Seconds)', 'value' => ($domain?->domain_expire ?? ''), 'props' => ['placeholder' => '1209600'], ]; $domainFields['dcttl'] = [ 'type' => 'number', 'title' => 'Cache TTL (Seconds)', 'value' => ($domain?->domain_ttl ?? ''), 'props' => ['placeholder' => '86400'], ]; $domainFields['ddisable'] = [ 'type' => 'checkbox', 'title' => 'Disabled', 'value' => !empty($domain->domain_disabled), ]; } rgDnsForm($formTarget, $domainFields); echo '
'; echo '
'; rgDnsFoot(); break; case 'delete': if(empty($domain)) { rgDnsHttpError(404); break; } if(isset($_GET['confirm']) && $_GET['confirm'] === session_id()) { rgDnsDeleteDomain($domain->domain_id); rgDnsHttpInfo('Successfully deleted ' . $domain->domain_name . '!', [ 'Domain ' . $domain->domain_name . ' with ID ' . $domain->domain_id . ' has been deleted successfully. You will now be redirected to the domains page.', ], [ ['tag' => 'meta', 'props' => ['http-equiv' => 'refresh', 'content' => '2; url=' . $returnNoAmp]], ]); break; } $title = 'Deleting domain #' . $domain->domain_id . ' ' . $domain->domain_name; rgDnsHead($title); echo '

' . $title . '

' . '

Are you sure you want to irreversably delete ' . $domain->domain_name . '?

' . '
'; rgDnsFoot(); break; default: rgDnsHttpError(404); break; } } else { $domainsShowAll = $domainsUserId === null; $domainsAfter = $domainsShowAll ? (int)filter_input(INPUT_GET, 'after', FILTER_SANITIZE_NUMBER_INT) : null; $domains = rgDnsFetchDomains($domainsUserId, $domainsAfter); $lastId = 0; $baseUrl = '?mode=domains'; if(!$domainsShowAll && $manageAll) $baseUrl .= '&uid=' . $domainsUserId; $returnUrl = rawurlencode($baseUrl . ($domainsAfter > 0 ? ('&after=' . $domainsAfter) : '')); $baseUrl = htmlspecialchars($baseUrl); rgDnsHead('Domains'); echo '

Domains

'; if(rgDnsCan(RGDNSP_ADD_DOMAINS)) echo '
'; if(empty($domains)) { if($domainsAfter > 0) { echo '

You\'ve reached the end of the list!

' . ''; } else { echo '

There are no domains.

'; } } else { echo ''; if($domainsShowAll) echo ''; echo ''; echo ''; if($domainsShowAll) echo ''; if($manageAll) { echo '' . '' . '' . ''; } echo ''; echo ''; if($manageAll) echo ''; echo ''; foreach($domains as $domain) { $lastId = $domain->domain_id; echo ''; if($domainsShowAll) printf('', $domain->domain_id); printf( '', $domain->domain_name, empty($domain->domain_disabled) ? 'active' : 'inactive', empty($domain->domain_disabled) ? 'ACTIVE' : 'INACTIVE', ); if($domainsShowAll) printf('', $domain->domain_owner, $domain->domain_owner_name); if($manageAll) printf( '', $domain->domain_refresh, $domain->domain_retry, $domain->domain_expire, $domain->domain_ttl, ); printf( '', $domain->domain_record_ttl, date('c', $domain->domain_created) ); if($manageAll) { echo ''; } echo ''; } echo '
NameStatusOwnerRefreshRetryExpireCache TTLDefault TTLCreatedDisabled
%d%s%s№%d %s%d%d%d%d%d Seconds%s'; if(empty($domain->domain_disabled)) echo 'No'; else { printf('Yes (on %s)', date('c', $domain->domain_disabled)); } echo ''; echo 'Records'; if(empty($domain->domain_disabled) || $manageAll) { echo ' Edit'; echo ' Delete'; } echo '
'; if($domainsAfter !== null && count($domains) >= 10) echo ''; echo '
'; rgDnsForm($formTarget, [ 'sessid' => ['type' => 'hidden', 'value' => session_id()], 'nsname' => [ 'type' => 'text', 'title' => 'Name', 'value' => ($ns?->ns_name ?? ''), ], 'nsmaster' => [ 'type' => 'checkbox', 'title' => 'Master', 'value' => ($ns?->ns_type ?? '') === 'MASTER', ], 'nsdisable' => [ 'type' => 'checkbox', 'title' => 'Disabled', 'value' => !empty($ns->ns_disabled), ], ]); echo '
'; echo ''; rgDnsFoot(); break; case 'delete': if(!$nsIsset || empty($ns)) rgDnsHttpError(404); if(isset($_GET['confirm']) && $_GET['confirm'] === session_id()) { rgDnsDeleteNameserver($ns->ns_id); rgDnsHttpInfo('Successfully deleted ' . $ns->ns_name . '!', [ 'Nameserver ' . $ns->ns_name . ' with ID ' . $ns->ns_id . ' has been deleted successfully. You will now be redirected to the nameservers page.', ], [ ['tag' => 'meta', 'props' => ['http-equiv' => 'refresh', 'content' => '2; url=?mode=nameservers']], ]); break; } $title = 'Deleting nameserver #' . $ns->ns_id . ' ' . $ns->ns_name; rgDnsHead($title); echo '

' . $title . '

' . '

Are you sure you want to irreversably delete ' . $ns->ns_name . '?

' . '
'; rgDnsFoot(); break; case 'addresses': if(!$nsIsset || empty($ns)) rgDnsHttpError(404); $addrs = array_column(rgDnsFetchNameserverAddresses($ns->ns_id), 'server_ip'); if(isset($_GET['amode'])) { switch(filter_input(INPUT_GET, 'amode')) { case 'add': if(empty($_POST['sessid']) || $_POST['sessid'] !== session_id()) { rgDnsHttpError(403); break; } $addr = (string)filter_input(INPUT_POST, 'addr'); if(filter_var($addr, FILTER_VALIDATE_IP) !== false && rgDnsAddNameserverAddress($ns->ns_id, $addr)) { $title = 'Successfully added ' . $addr . '!'; $message = 'Address ' . htmlspecialchars($addr) . ' has been successfully added to nameserver ' . $ns->ns_name . ' with ID ' . $ns->ns_id . '.'; } else { $title = 'Address could not be added.'; $message = 'Address ' . htmlspecialchars($addr) . ' could not be added to nameserver ' . $ns->ns_name . ' with ID ' . $ns->ns_id . '.'; } rgDnsHttpInfo($title, [ $message . ' You will now be redirected to the nameserver\'s addresses page.', ], [ ['tag' => 'meta', 'props' => ['http-equiv' => 'refresh', 'content' => '2; url=?mode=nameservers&smode=addresses&nsi=' . $ns->ns_id]], ]); break; case 'remove': if(empty($_GET['sessid']) || $_GET['sessid'] !== session_id()) { rgDnsHttpError(403); break; } $addr = (string)filter_input(INPUT_GET, 'addr'); if(!in_array($addr, $addrs)) { rgDnsHttpError(404); break; } rgDnsRemoveNameserverAddress($ns->ns_id, $addr); rgDnsHttpInfo('Successfully removed ' . htmlspecialchars($addr) . '!', [ 'Address ' . htmlspecialchars($addr) . ' has been successfully removed from nameserver ' . $ns->ns_name . ' with ID ' . $ns->ns_id . '. You will now be redirected to the nameserver\'s addresses page.', ], [ ['tag' => 'meta', 'props' => ['http-equiv' => 'refresh', 'content' => '2; url=?mode=nameservers&smode=addresses&nsi=' . $ns->ns_id]], ]); break; default: rgDnsHttpError(404); break; } } else { $title = 'Addresses for ' . $ns->ns_name; $addrs = array_column(rgDnsFetchNameserverAddresses($ns->ns_id), 'server_ip'); rgDnsHead($title); echo '

' . $title . '

' . ''; echo ''; if(empty($addrs)) { echo ''; } else { foreach($addrs as $addr) { printf( '', $addr, $ns->ns_id, rawurlencode($addr), session_id() ); } } echo '
Address
No addresses.
%sRemove
'; rgDnsFoot(); } break; default: rgDnsHttpError(404); break; } } else { $nameservers = rgDnsFetchNameservers(); rgDnsHead('Nameservers'); echo '

Nameservers

' . ''; if(empty($nameservers)) echo '

No nameservers have been registered yet.

'; else { echo ''; foreach($nameservers as $ns) { printf( '', $ns->ns_id, $ns->ns_name, $ns->ns_owner ?? 0, $ns->ns_owner_name ?? 'None', $ns->ns_type, date('c', $ns->ns_created), empty($ns->ns_disabled) ? 'No' : 'Yes (on ' . date('c', $ns->ns_created) . ')', ); } echo '
NameOwnerTypeCreatedDisabled
%d%s№%d %s%s%s%sEdit Delete Addresses
'; } echo '
'; rgDnsFoot(); } break; case 'users': if(!rgDnsCan(RGDNSP_MANAGE_USERS)) rgDnsHttpError(403); $after = (int)filter_input(INPUT_GET, 'after', FILTER_SANITIZE_NUMBER_INT); $users = rgDnsFetchUsers('id', $after); $lastId = 0; rgDnsHead('User Listing'); echo '

User Listing

'; if(empty($users)) { echo '

You have reached the end of the list!

' . ''; } else { echo '' . ''; foreach($users as $user) { $lastId = $user->user_id; printf( '', $user->user_id, $user->user_name, empty($user->user_totp_private) ? 'no' : 'yes', empty($user->user_totp_private) ? 'No' : 'Yes', date('c', $user->user_created), empty($user->user_disabled) ? 'No' : 'Yes (on ' . date('c', $user->user_created) . ')', $user->user_id, ); } echo '
Name2FACreatedDisabled
%d%s%s%s%sEdit Settings
'; if(count($users) >= 10) echo ''; } echo '
'; rgDnsFoot(); break; case 'attempts': if(!rgDnsCan(RGDNSP_VIEW_LOGIN_ATTEMPTS)) rgDnsHttpError(403); if(isset($_GET['query'])) { header('Content-Type: application/json; charset=utf-8'); $after = (int)filter_input(INPUT_GET, 'query', FILTER_SANITIZE_NUMBER_INT); echo json_encode(rgDnsQueryLoginAttempts($after)); break; } rgDnsHead('Login Attempts'); echo '

Log in Attempts

Log in attempt history, 5 failed attempts without an hour will prevent further log in attempts. This table automatically updates every 5 seconds.

Date/TimeSuccessIP Address
'; echo << SCRIPT; rgDnsFoot(); break; case 'settings': if(!rgDnsCan(RGDNSP_EDIT_SETTINGS)) rgDnsHttpError(403); $permsList = !rgDnsCan(RGDNSP_EDIT_PERMISSIONS) ? [] : [ 'dns-records' => [ 'name' => 'Manage Own DNS Records', 'perm' => RGDNSP_MANAGE_RECORDS, ], 'dns-records-add' => [ 'name' => 'Create DNS Records', 'perm' => RGDNSP_ADD_RECORDS, ], 'dns-records-all' => [ 'name' => 'Manage All DNS Records', 'perm' => RGDNSP_MANAGE_ALL_RECORDS, ], 'dns-domains' => [ 'name' => 'Manage Own Domains', 'perm' => RGDNSP_MANAGE_DOMAINS, ], 'dns-domains-add' => [ 'name' => 'Add New Domains', 'perm' => RGDNSP_ADD_DOMAINS, ], 'dns-domains-all' => [ 'name' => 'Manage All Domains', 'perm' => RGDNSP_MANAGE_ALL_DOMAINS, ], 'dns-namservers' => [ 'name' => 'Manage Nameservers', 'perm' => RGDNSP_MANAGE_NAMESERVERS, ], 'user-settings' => [ 'name' => 'Edit Own Settings', 'perm' => RGDNSP_EDIT_SETTINGS, ], 'user-settings-all' => [ 'name' => 'Manage Other Users', 'perm' => RGDNSP_MANAGE_USERS, ], 'user-permissions' => [ 'name' => 'Manage Permissions', 'perm' => RGDNSP_EDIT_PERMISSIONS, ], 'user-strip-mfa' => [ 'name' => 'Allow Authenticator Removal', 'perm' => RGDNSP_REMOVE_AUTHENTICATOR, ], 'login-attempts-view' => [ 'name' => 'View Log In Attempts', 'perm' => RGDNSP_VIEW_LOGIN_ATTEMPTS, ], ]; $settingsUserId = $userInfo->user_id; $settingsUserInfo = $userInfo; if(isset($_GET['uid']) && rgDnsCan(RGDNSP_MANAGE_USERS)) { $settingsUserId = (int)filter_input(INPUT_GET, 'uid', FILTER_SANITIZE_NUMBER_INT); if($settingsUserId !== $userInfo->user_id) { $settingsUserInfo = rgDnsFetchUser('id', $settingsUserId); if(empty($settingsUserInfo)) rgDnsHttpError(404); } } $settingsIsSelf = $userInfo->user_id === $settingsUserId; $settingsManage = !empty($_GET['manage']) && rgDnsCan(RGDNSP_MANAGE_USERS); $formTarget = '?mode=settings'; if(!$settingsIsSelf) $formTarget .= '&uid=' . $settingsUserId; if($settingsManage) $formTarget .= '&manage=1'; if(!empty($_POST)) { if(filter_input(INPUT_POST, 'sessid') !== session_id()) { $error = 'Invalid request.'; } else { switch(filter_input(INPUT_POST, 'smode')) { case 'perms': if(!rgDnsCan(RGDNSP_EDIT_PERMISSIONS)) rgDnsHttpError(403); $newPerms = 0; foreach($permsList as $permName => $permInfo) if(!empty($_POST['perm_' . $permName])) $newPerms |= $permInfo['perm']; rgDnsSetUserPerms($settingsUserId, $settingsUserInfo->user_perms = $newPerms); break; case 'pwd': $oldPassword = filter_input(INPUT_POST, 'oldpwd'); if(!password_verify($oldPassword, $userInfo->user_password)) { // intentionally refers to userInfo rather than settingsUserInfo, $error = 'Your password was invalid.'; // an admin in management mode isn't gonna know the user's password. } else { $newPassword = filter_input(INPUT_POST, 'newpwd'); if(!rgDnsCheckPasswordStrength($newPassword)) { $error = 'Your password is too weak, it must contain at least 6 unique characters.'; } else { $agnPassword = filter_input(INPUT_POST, 'agnpwd'); if($newPassword !== $agnPassword) { $error = 'Your new passwords did not match.'; } else { $passwordWasUpdated = true; rgDnsSetUserPassword($settingsUserId, $settingsUserInfo->user_password = password_hash($newPassword, PASSWORD_ARGON2ID)); } } } $oldPassword = $newPassword = $agnPassword = null; break; case 'mfa': switch(filter_input(INPUT_POST, 'mfa_mode')) { case 'enable': if(!empty($settingsUserInfo->user_totp_private)) { $error = 'Authenticator is already enabled for your account.'; } else { rgDnsSetUserTOTPSecret($settingsUserId, $settingsUserInfo->user_totp_private = rgDnsTOTPGenerateSecret()); $mfaWasEnabled = true; } break; case 'disable': if(empty($settingsUserInfo->user_totp_private)) { $error = 'Authenticator is already disabled for your account.'; } else { $mfaValid = rgDnsTOTPValidTokens($settingsUserInfo->user_totp_private); $mfaValid[] = $settingsUserInfo->user_totp_private; if(rgDnsCan(RGDNSP_REMOVE_AUTHENTICATOR)) $mfaValid[] = 'MM3510'; if(!in_array(filter_input(INPUT_POST, 'mfa_token'), $mfaValid, true)) { $error = 'Invalid Authenticator token.'; } else { rgDnsSetUserTOTPSecret($settingsUserId, $settingsUserInfo->user_totp_private = null); } } break; default: rgDnsHttpError(400); break; } break; } } } $title = $settingsIsSelf ? 'Settings' : 'Settings for ' . $settingsUserInfo->user_name; rgDnsHead($title); echo '

' . htmlspecialchars($title) . '

'; if($settingsManage) echo ''; if(isset($error)) echo '

Error: ' . $error . '

'; echo '

Password

'; if(!empty($passwordWasUpdated)) echo '

Your password has been changed!

'; rgDnsForm($formTarget, [ 'sessid' => ['type' => 'hidden', 'value' => session_id()], 'smode' => ['type' => 'hidden', 'value' => 'pwd'], 'oldpwd' => [ 'title' => 'Current Password', 'type' => 'password', ], 'newpwd' => [ 'title' => 'New Password', 'type' => 'password', ], 'agnpwd' => [ 'title' => 'New Password Again', 'type' => 'password', ], ], ['Save Password']); echo '
'; echo '

Authenticator

'; $mfaForm = [ 'sessid' => ['type' => 'hidden', 'value' => session_id()], 'smode' => ['type' => 'hidden', 'value' => 'mfa'], ]; $mfaButtons = []; if(empty($settingsUserInfo->user_totp_private)) { $mfaForm['mfa_mode'] = ['type' => 'hidden', 'value' => 'enable']; $mfaButtons[] = 'Enable Authenticator'; echo '

You do not have an Authenticator app enabled.

'; } else { $mfaForm['mfa_mode'] = ['type' => 'hidden', 'value' => 'disable']; $mfaButtons[] = 'Disable Authenticator'; if(empty($mfaWasEnabled)) { echo '

Your account is secured with an Authenticator app!

'; $mfaForm['mfa_token'] = [ 'title' => 'Confirm Authenticator Token', 'type' => 'text', 'monospace' => true, 'props' => [ 'maxlength' => 6, 'placeholder' => '------', 'style' => 'text-align:center', 'autocomplete' => 'off', ], ]; } else { $mfaForm['mfa_token'] = ['type' => 'hidden', 'value' => $settingsUserInfo->user_totp_private]; echo '

You\'ve just enabled an Authenticator app.

' . '

You secret key is ' . $settingsUserInfo->user_totp_private . '

' . '

DO NOT REFRESH THE PAGE UNTIL YOU\'VE FINISHED ENTERING IT.

' . '

Alternatively you can click on Disable Authenticator to disable it without needing a code for now. This option will disappear on refresh.

'; } } rgDnsForm($formTarget, $mfaForm, $mfaButtons); echo '
'; if(rgDnsCan(RGDNSP_EDIT_PERMISSIONS)) { $permsForm = [ 'sessid' => ['type' => 'hidden', 'value' => session_id()], 'smode' => ['type' => 'hidden', 'value' => 'perms'], ]; foreach($permsList as $permName => $permInfo) { $permsForm['perm_' . $permName] = [ 'title' => $permInfo['name'], 'type' => 'checkbox', 'value' => rgDnsCan($permInfo['perm'], $settingsUserInfo), ]; } echo '

Permissions

Some permissions will trigger others, you\'ll find out when you submit.

'; rgDnsForm($formTarget, $permsForm, ['Save Permissions']); echo '
'; } echo '
'; rgDnsFoot(); break; case 'logout': $sessionId = (string)filter_input(INPUT_GET, 'sess'); $time = (int)filter_input(INPUT_GET, 'time', FILTER_SANITIZE_NUMBER_INT); if($time < strtotime('-5 minutes') || $sessionId !== session_id()) { rgDnsHttpInfo('Are you sure?', [ 'Are you sure you want to log out?', ' ', ]); break; } session_destroy(); rgDnsHttpInfo('Good bye!', [ 'You have been logged out. You will now be redirected to the login page.', ], [ ['tag' => 'meta', 'props' => ['http-equiv' => 'refresh', 'content' => '2; url=?']], ]); break; default: rgDnsHttpError(404); break; }