Initial commit

This commit is contained in:
Rekryt
2024-08-30 15:22:24 +03:00
commit 171e449744
46 changed files with 4638 additions and 0 deletions

View File

@@ -0,0 +1,69 @@
<?php
namespace OpenCCK\Domain\Helper;
use Amp\Dns\DnsConfig;
use Amp\Dns\DnsConfigLoader;
use Amp\Dns\DnsException;
use Amp\Dns\DnsRecord;
use Amp\Dns\HostLoader;
use Amp\Dns\Rfc1035StubDnsResolver;
use OpenCCK\Infrastructure\API\App;
use function Amp\Dns\dnsResolver;
use function Amp\Dns\resolve;
readonly class DNSHelper {
public function __construct(private array $dnsServers = []) {
}
/**
* @param array $dnsServers
* @return void
*/
private function setResolver(array $dnsServers): void {
dnsResolver(
new Rfc1035StubDnsResolver(
null,
new class ($dnsServers) implements DnsConfigLoader {
public function __construct(private readonly array $dnsServers = []) {
}
public function loadConfig(): DnsConfig {
return new DnsConfig($this->dnsServers, (new HostLoader())->loadHosts());
}
}
)
);
}
/**
* @param string $domain
* @return array[]
*/
public function resolve(string $domain): array {
$ipv4 = [];
$ipv6 = [];
foreach ($this->dnsServers as $server) {
$this->setResolver([$server]);
try {
$ipv4 = array_merge(
$ipv4,
array_map(fn(DnsRecord $record) => $record->getValue(), resolve($domain, DnsRecord::A))
);
} catch (DnsException $e) {
App::getLogger()->error($e->getMessage(), [$server]);
}
try {
$ipv6 = array_merge(
$ipv6,
array_map(fn(DnsRecord $record) => $record->getValue(), resolve($domain, DnsRecord::AAAA))
);
} catch (DnsException $e) {
App::getLogger()->error($e->getMessage(), [$server]);
}
}
App::getLogger()->debug('resolve: ' . $domain, [count($ipv4), count($ipv6)]);
return [$ipv4, $ipv6];
}
}

View File

@@ -0,0 +1,144 @@
<?php
namespace OpenCCK\Domain\Helper;
use OpenCCK\Infrastructure\API\App;
use OpenCCK\Infrastructure\Storage\CIDRStorage;
use function Amp\async;
use function Amp\delay;
class IP4Helper {
public static function processCIDR(array $ips, $results = []): array {
$count = count($ips);
foreach ($ips as $i => $ip) {
if ($ip === '127.0.0.1') {
continue;
}
async(function () use ($ip, $i, $count, &$results) {
if (CIDRStorage::getInstance()->has($ip)) {
$searchArray = CIDRStorage::getInstance()->get($ip);
$results = array_merge($results, self::trimCIDRs($searchArray));
App::getLogger()->debug($ip . ' -> ' . json_encode($searchArray), [$i + 1 . '/' . $count]);
return;
}
if (self::isInRange($ip, $results)) {
return;
}
$search = null;
$result = shell_exec('whois ' . $ip . ' | grep CIDR');
if ($result) {
preg_match('/^CIDR:\s*(.*)$/m', $result, $matches);
$search = $matches[1] ?? null;
}
if (!$search) {
$search = shell_exec(
implode(' | ', [
'whois -a ' . $ip,
'grep inetnum',
'head -n 1',
"awk '{print $2\"-\"$4}'",
'sed "s/-$//"',
'xargs ipcalc',
"grep -oE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+$'",
])
);
}
if (!$search) {
$search = shell_exec(
implode(' | ', [
'whois -a ' . $ip,
'grep IPv4',
'grep " - "',
'head -n 1',
"awk '{print $3\"-\"$5}'",
'xargs ipcalc',
"grep -oE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+$'",
])
);
}
if ($search) {
$search = strtr($search, ["\n" => ' ', ', ' => ' ']);
$searchArray = array_filter(
explode(' ', strtr($search, ' ', '')),
fn(string $cidr) => strlen($cidr) > 0
);
CIDRStorage::getInstance()->set($ip, $searchArray);
$results = array_merge($results, self::trimCIDRs($searchArray));
App::getLogger()->debug($ip . ' -> ' . json_encode($searchArray), [$i + 1 . '/' . $count]);
} else {
App::getLogger()->error($ip . ' -> CIDR not found', [$i + 1 . '/' . $count]);
}
delay(0.001);
})->await();
}
return self::minimizeSubnets($results);
}
public static function trimCIDRs(array $searchArray): array {
$subnets = [];
foreach ($searchArray as $search) {
foreach (explode(' ', $search) as $cidr) {
if (str_contains($cidr, '/')) {
$subnets[] = trim($cidr);
}
}
}
return $subnets;
}
public static function sortSubnets(array $subnets): array {
usort($subnets, function ($a, $b) {
return (int) explode('/', $a)[1] - (int) explode('/', $b)[1];
});
usort($subnets, function ($a, $b) {
return ip2long(explode('/', $a)[0]) - ip2long(explode('/', $b)[0]);
});
return $subnets;
}
public static function minimizeSubnets(array $subnets): array {
$result = [];
foreach (self::sortSubnets(array_filter($subnets, fn(string $subnet) => !!$subnet)) as $subnet) {
$include = true;
[$ip /*, $mask*/] = explode('/', $subnet);
$ipLong = ip2long($ip);
// $maskLong = ~((1 << 32 - (int) $mask) - 1);
foreach ($result as $resSubnet) {
[$resIp, $resMask] = explode('/', $resSubnet);
$resIpLong = ip2long($resIp);
$resMaskLong = ~((1 << 32 - (int) $resMask) - 1);
if (($ipLong & $resMaskLong) === ($resIpLong & $resMaskLong)) {
$include = false;
break;
}
}
if ($include) {
$result[] = $subnet;
}
}
return $result;
}
public static function isInRange(string $ip, array $cidrs): bool {
foreach ($cidrs as $cidr) {
[$subnet, $mask] = explode('/', $cidr);
if ((ip2long($ip) & ~((1 << 32 - $mask) - 1)) === ip2long($subnet)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,176 @@
<?php
namespace OpenCCK\Domain\Helper;
use OpenCCK\Infrastructure\API\App;
use OpenCCK\Infrastructure\Storage\CIDRStorage;
use function Amp\async;
use function Amp\delay;
class IP6Helper {
public static function processCIDR(array $ips, $results = []): array {
$count = count($ips);
foreach ($ips as $i => $ip) {
if ($ip === '::1') {
continue;
}
async(function () use ($ip, $i, $count, &$results) {
if (CIDRStorage::getInstance()->has($ip)) {
$searchArray = CIDRStorage::getInstance()->get($ip);
$results = array_merge($results, self::trimCIDRs($searchArray));
App::getLogger()->debug($ip . ' -> ' . json_encode($searchArray), [$i + 1 . '/' . $count]);
return;
}
if (self::isInRange($ip, $results)) {
return;
}
$search = shell_exec(
implode(' | ', [
'whois -a ' . $ip,
'grep inet6num',
'grep -v "/0"',
'head -n 1',
"awk '{print $2}'",
])
);
if (!$search) {
$search = shell_exec(
implode(' | ', [
'whois -a ' . $ip,
'grep route6',
'grep -v "/0"',
'head -n 1',
"awk '{print $2}'",
])
);
}
if ($search) {
$search = strtr($search, ["\n" => ' ', ', ' => ' ']);
$searchArray = array_filter(
explode(' ', strtr($search, ' ', '')),
fn(string $cidr) => strlen($cidr) > 0
);
CIDRStorage::getInstance()->set($ip, $searchArray);
$results = array_merge($results, self::trimCIDRs($searchArray));
App::getLogger()->debug($ip . ' -> ' . json_encode($searchArray), [$i + 1 . '/' . $count]);
} else {
App::getLogger()->error($ip . ' -> CIDR not found', [$i + 1 . '/' . $count]);
}
delay(0.001);
})->await();
}
//return self::minimizeSubnets($results);
return $results;
}
/**
* @param array $searchArray
* @return array
*/
public static function trimCIDRs(array $searchArray): array {
$subnets = [];
foreach ($searchArray as $search) {
foreach (explode(' ', $search) as $cidr) {
if (str_contains($cidr, '/')) {
[$address, $prefix] = explode('/', trim($cidr));
$subnets[] = $address . '/' . max($prefix, 64);
}
}
}
return $subnets;
}
/**
* @param array $subnets
* @return array
*/
public static function sortSubnets(array $subnets): array {
usort($subnets, function ($a, $b) {
return (int) explode('/', $a)[1] - (int) explode('/', $b)[1];
});
usort($subnets, function ($a, $b) {
$ipA = inet_pton(explode('/', $a)[0]);
$ipB = inet_pton(explode('/', $b)[0]);
return strcmp($ipA, $ipB);
});
return $subnets;
}
/**
* @param array $subnets
* @return array
*/
public static function minimizeSubnets(array $subnets): array {
$result = [];
foreach (self::sortSubnets(array_filter($subnets, fn(string $subnet) => !!$subnet)) as $subnet) {
if (!$subnet) {
continue;
}
[$address, $prefix] = explode('/', $subnet);
$addressNum = inet_pton($address);
$addressNum = unpack('J', $addressNum)[1];
$isUnique = true;
foreach ($result as $existingCidr) {
[$existingAddress, $existingPrefix] = explode('/', $existingCidr);
$existingAddressNum = inet_pton($existingAddress);
$existingAddressNum = unpack('J', $existingAddressNum)[1];
$mask = (1 << 128) - (1 << 128 - $prefix);
$existingMask = (1 << 128) - (1 << 128 - $existingPrefix);
if (($addressNum & $mask) === ($existingAddressNum & $existingMask)) {
$isUnique = false;
break;
}
}
if ($isUnique) {
$result[] = $subnet;
}
}
return $result;
}
public static function isInRange(string $ip, array $cidrs): bool {
$ip = inet_pton($ip);
foreach ($cidrs as $cidr) {
[$subnet, $mask] = explode('/', $cidr);
$subnet = inet_pton($subnet);
$mask = intval($mask);
$binaryMask = str_repeat('f', $mask >> 2);
switch ($mask % 4) {
case 1:
$binaryMask .= '8';
break;
case 2:
$binaryMask .= 'c';
break;
case 3:
$binaryMask .= 'e';
break;
}
$binaryMask = str_pad($binaryMask, 32, '0');
$mask = pack('H*', $binaryMask);
if (($ip & $mask) === ($subnet & $mask)) {
return true;
}
}
return false;
}
}