From 09812cd3e8260a74f1f344e56cc52389f0d6f7d0 Mon Sep 17 00:00:00 2001 From: Rekryt Date: Sat, 2 Nov 2024 05:23:56 +0300 Subject: [PATCH] fix: Merge adjacent CIDRs --- src/Domain/Entity/Site.php | 6 ++-- src/Domain/Factory/SiteFactory.php | 8 +++-- src/Domain/Helper/IP4Helper.php | 19 +++++++--- src/Domain/Helper/IP6Helper.php | 58 ++++++++++++++++++------------ 4 files changed, 58 insertions(+), 33 deletions(-) diff --git a/src/Domain/Entity/Site.php b/src/Domain/Entity/Site.php index bdbec89..563b73d 100644 --- a/src/Domain/Entity/Site.php +++ b/src/Domain/Entity/Site.php @@ -145,15 +145,15 @@ final class Site { if (isset($this->external->cidr4) && $this->isUseIpv4) { foreach ($this->external->cidr4 as $url) { - $this->cidr4 = SiteFactory::normalize( - array_merge($this->cidr4, explode("\n", file_get_contents($url))), - true + $this->cidr4 = IP4Helper::minimizeSubnets( + SiteFactory::normalize(array_merge($this->cidr4, explode("\n", file_get_contents($url))), true) ); } } if (isset($this->external->cidr6) && $this->isUseIpv6) { foreach ($this->external->cidr6 as $url) { + // todo IP6Helper::minimizeSubnets $this->cidr6 = SiteFactory::normalize( array_merge($this->cidr6, explode("\n", file_get_contents($url))), true diff --git a/src/Domain/Factory/SiteFactory.php b/src/Domain/Factory/SiteFactory.php index b2e383c..bb2c8e6 100644 --- a/src/Domain/Factory/SiteFactory.php +++ b/src/Domain/Factory/SiteFactory.php @@ -3,10 +3,12 @@ namespace OpenCCK\Domain\Factory; use OpenCCK\Domain\Entity\Site; +use OpenCCK\Domain\Helper\IP4Helper; +use OpenCCK\Domain\Helper\IP6Helper; use OpenCCK\Infrastructure\API\App; use stdClass; -use function \OpenCCK\getEnv; +use function OpenCCK\getEnv; class SiteFactory { // prettier-ignore @@ -78,8 +80,8 @@ class SiteFactory { $domains = self::normalize($domains); $ip4 = self::normalize($ip4, true); $ip6 = self::normalize($ip6, true); - $cidr4 = self::normalize($cidr4, true); - $cidr6 = self::normalize($cidr6, true); + $cidr4 = IP4Helper::minimizeSubnets(self::normalize($cidr4, true)); + $cidr6 = self::normalize($cidr6, true); //$cidr6 = IP6Helper::minimizeSubnets(self::normalize($cidr6, true)); return new Site($name, $group, $domains, $dns, $timeout, $ip4, $ip6, $cidr4, $cidr6, $external); } diff --git a/src/Domain/Helper/IP4Helper.php b/src/Domain/Helper/IP4Helper.php index 357e5d0..0d88d2c 100644 --- a/src/Domain/Helper/IP4Helper.php +++ b/src/Domain/Helper/IP4Helper.php @@ -22,7 +22,7 @@ class IP4Helper { if (CIDRStorage::getInstance()->has($ip)) { $searchArray = CIDRStorage::getInstance()->get($ip); - $results = array_merge($results, self::trimCIDRs($searchArray)); + $results = array_merge($results, $searchArray); App::getLogger()->debug($ip . ' -> ' . json_encode($searchArray), [ $i + 1 . '/' . $count, @@ -72,8 +72,12 @@ class IP4Helper { explode(' ', strtr($search, ' ', '')), fn(string $cidr) => strlen($cidr) > 0 ); + $searchArray = array_values( + array_filter(self::trimCIDRs($searchArray), fn($cidr) => self::isInCIDR($ip, $cidr)) + ); + CIDRStorage::getInstance()->set($ip, $searchArray); - $results = array_merge($results, self::trimCIDRs($searchArray)); + $results = array_merge($results, $searchArray); App::getLogger()->debug($ip . ' -> ' . json_encode($searchArray), [$i + 1 . '/' . $count, 'found']); } else { @@ -135,10 +139,17 @@ class IP4Helper { return $result; } + public static function isInCIDR(string $ip, string $cidr): bool { + [$subnet, $mask] = explode('/', $cidr); + if ((ip2long($ip) & ~((1 << 32 - $mask) - 1)) === ip2long($subnet)) { + return true; + } + return false; + } + 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)) { + if (self::isInCIDR($ip, $cidr)) { return true; } } diff --git a/src/Domain/Helper/IP6Helper.php b/src/Domain/Helper/IP6Helper.php index 1866aeb..58e1e24 100644 --- a/src/Domain/Helper/IP6Helper.php +++ b/src/Domain/Helper/IP6Helper.php @@ -22,7 +22,7 @@ class IP6Helper { if (CIDRStorage::getInstance()->has($ip)) { $searchArray = CIDRStorage::getInstance()->get($ip); - $results = array_merge($results, self::trimCIDRs($searchArray)); + $results = array_merge($results, $searchArray); App::getLogger()->debug($ip . ' -> ' . json_encode($searchArray), [ $i + 1 . '/' . $count, @@ -72,8 +72,12 @@ class IP6Helper { explode(' ', strtr($search, ' ', '')), fn(string $cidr) => strlen($cidr) > 0 ); + $searchArray = array_values( + array_filter(self::trimCIDRs($searchArray), fn($cidr) => self::isInCIDR($ip, $cidr)) + ); + CIDRStorage::getInstance()->set($ip, $searchArray); - $results = array_merge($results, self::trimCIDRs($searchArray)); + $results = array_merge($results, $searchArray); App::getLogger()->debug($ip . ' -> ' . json_encode($searchArray), [$i + 1 . '/' . $count, 'found']); } else { @@ -160,30 +164,38 @@ class IP6Helper { return $result; } - public static function isInRange(string $ip, array $cidrs): bool { + public static function isInCidr(string $ip, string $cidr): bool { $ip = inet_pton($ip); + [$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; + } + + public static function isInRange(string $ip, array $cidrs): bool { 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)) { + if (self::isInCIDR($ip, $cidr)) { return true; } }