154 lines
No EOL
5.5 KiB
PHP
154 lines
No EOL
5.5 KiB
PHP
<?php
|
|
|
|
namespace Socialbox\Classes;
|
|
|
|
use InvalidArgumentException;
|
|
use Socialbox\Exceptions\DatabaseOperationException;
|
|
use Socialbox\Exceptions\ResolutionException;
|
|
use Socialbox\Managers\ResolvedDnsRecordsManager;
|
|
use Socialbox\Objects\DnsRecord;
|
|
|
|
class ServerResolver
|
|
{
|
|
private static array $mockedRecords = [];
|
|
|
|
/**
|
|
* Resolves a domain by retrieving and parsing its DNS TXT records.
|
|
* Optionally checks a database for cached resolution data before performing a DNS query.
|
|
*
|
|
* @param string $domain The domain name to resolve.
|
|
* @param bool $useDatabase Whether to check the database for cached resolution data; defaults to true.
|
|
* @return DnsRecord The parsed DNS record for the given domain.
|
|
* @throws ResolutionException If the DNS TXT records cannot be retrieved or parsed.
|
|
* @throws DatabaseOperationException If an error occurs while interacting with the database. (Only if $useDatabase is true)
|
|
*/
|
|
public static function resolveDomain(string $domain, bool $useDatabase=true): DnsRecord
|
|
{
|
|
// Return the mocked record if the mocking record is set
|
|
if(isset(self::$mockedRecords[$domain]))
|
|
{
|
|
return self::$mockedRecords[$domain];
|
|
}
|
|
|
|
// Return the mocked record from the configuration if one is set
|
|
if(isset(Configuration::getInstanceConfiguration()->getDnsMocks()[$domain]))
|
|
{
|
|
return DnsHelper::parseTxt(Configuration::getInstanceConfiguration()->getDnsMocks()[$domain]);
|
|
}
|
|
|
|
// Check the database if enabled
|
|
if ($useDatabase)
|
|
{
|
|
// Return from the database cache if one exists
|
|
// TODO: Implement renewal here
|
|
$resolvedServer = ResolvedDnsRecordsManager::getDnsRecord($domain);
|
|
if ($resolvedServer !== null)
|
|
{
|
|
return $resolvedServer;
|
|
}
|
|
}
|
|
|
|
// Resolve DNS & Records
|
|
$txtRecords = self::dnsGetTxtRecords($domain);
|
|
if ($txtRecords === false)
|
|
{
|
|
throw new ResolutionException(sprintf("Failed to resolve DNS TXT records for %s", $domain));
|
|
}
|
|
$fullRecord = self::concatenateTxtRecords($txtRecords);
|
|
|
|
try
|
|
{
|
|
// Parse the TXT record using DnsHelper
|
|
$record = DnsHelper::parseTxt($fullRecord);
|
|
|
|
// Cache the resolved server record in the database
|
|
if($useDatabase)
|
|
{
|
|
ResolvedDnsRecordsManager::addResolvedServer($domain, $record);
|
|
}
|
|
|
|
return $record;
|
|
}
|
|
catch (InvalidArgumentException $e)
|
|
{
|
|
throw new ResolutionException(sprintf("Failed to find valid SocialBox record for %s: %s", $domain, $e->getMessage()));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieves the TXT records for a given domain using the dns_get_record function.
|
|
*
|
|
* @param string $domain The domain name to fetch TXT records for.
|
|
* @return array|false An array of DNS TXT records on success, or false on failure.
|
|
*/
|
|
private static function dnsGetTxtRecords(string $domain): array|false
|
|
{
|
|
return @dns_get_record($domain, DNS_TXT);
|
|
}
|
|
|
|
/**
|
|
* Concatenates an array of TXT records into a single string, filtering for SocialBox records.
|
|
*
|
|
* @param array $txtRecords An array of TXT records, where each record is expected to have a 'txt' key.
|
|
* @return string A concatenated string of all relevant TXT records.
|
|
*/
|
|
private static function concatenateTxtRecords(array $txtRecords): string
|
|
{
|
|
$fullRecordBuilder = '';
|
|
foreach ($txtRecords as $txt)
|
|
{
|
|
if (isset($txt['txt']))
|
|
{
|
|
$record = trim($txt['txt'], '" ');
|
|
// Only include records that start with v=socialbox
|
|
if (stripos($record, 'v=socialbox') === 0)
|
|
{
|
|
$fullRecordBuilder .= $record;
|
|
}
|
|
}
|
|
}
|
|
return $fullRecordBuilder;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the mocked records.
|
|
*
|
|
* @return array The list of mocked records.
|
|
*/
|
|
public static function getMockedRecords(): array
|
|
{
|
|
return self::$mockedRecords;
|
|
}
|
|
|
|
/**
|
|
* Adds a mock DNS record for a specific domain.
|
|
*
|
|
* @param string $domain The domain name for which the DNS record is being mocked.
|
|
* @param DnsRecord $record The DNS record to be associated with the specified domain.
|
|
* @return void
|
|
*/
|
|
public static function addMock(string $domain, DnsRecord|string $record): void
|
|
{
|
|
if(isset(self::$mockedRecords[$domain]))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(is_string($record))
|
|
{
|
|
$record = DnsHelper::parseTxt($record);
|
|
}
|
|
|
|
self::$mockedRecords[$domain] = $record;
|
|
}
|
|
|
|
/**
|
|
* Clears all mocked records by resetting the mocked records array.
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function clearMockedRecords(): void
|
|
{
|
|
self::$mockedRecords = [];
|
|
}
|
|
} |