From 7d82c82fd98b4496dac88154b5ed034f5b5276db Mon Sep 17 00:00:00 2001 From: LinQhost Managed hosting Date: Tue, 4 May 2021 10:29:25 +0200 Subject: [PATCH 01/14] Make it possible to exempt ips from the rate-limiter --- cfg/conf.sample.php | 4 ++ composer.json | 5 ++- composer.lock | 70 +++++++++++++++++++++++++++++- lib/Configuration.php | 1 + lib/Persistence/TrafficLimiter.php | 51 ++++++++++++++++++++++ 5 files changed, 128 insertions(+), 3 deletions(-) diff --git a/cfg/conf.sample.php b/cfg/conf.sample.php index e958c88..0837645 100644 --- a/cfg/conf.sample.php +++ b/cfg/conf.sample.php @@ -135,6 +135,10 @@ markdown = "Markdown" ; Set this to 0 to disable rate limiting. limit = 10 +; Set ips (v4|v6) which should be exempted for the rate-limit. CIDR also supported. Needed to be comma separated. +; Unset for enabling and invalid values will be ignored +; eg: exemptedIp = '1.2.3.4,10.10.10/24' + ; (optional) if your website runs behind a reverse proxy or load balancer, ; set the HTTP header containing the visitors IP address, i.e. X_FORWARDED_FOR ; header = "X_FORWARDED_FOR" diff --git a/composer.json b/composer.json index 899c863..ff79af9 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,8 @@ "require" : { "php" : "^5.6.0 || ^7.0 || ^8.0", "paragonie/random_compat" : "2.0.19", - "yzalis/identicon" : "2.0.0" + "yzalis/identicon" : "2.0.0", + "mlocati/ip-lib": "^1.14" }, "require-dev" : { "phpunit/phpunit" : "^4.6 || ^5.0" @@ -39,4 +40,4 @@ "config" : { "autoloader-suffix" : "DontChange" } -} \ No newline at end of file +} diff --git a/composer.lock b/composer.lock index 80d2276..66a9c8a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,76 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9d110873bf15a6abd66734e8a818134c", + "content-hash": "8138b0e4bb3fcaab9ca4c02bbe35d891", "packages": [ + { + "name": "mlocati/ip-lib", + "version": "1.14.0", + "source": { + "type": "git", + "url": "https://github.com/mlocati/ip-lib.git", + "reference": "882bc0e115970a536b13bcfa59f312783fce08c8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mlocati/ip-lib/zipball/882bc0e115970a536b13bcfa59f312783fce08c8", + "reference": "882bc0e115970a536b13bcfa59f312783fce08c8", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "ext-pdo_sqlite": "*", + "phpunit/dbunit": "^1.4 || ^2 || ^3 || ^4", + "phpunit/phpunit": "^4.8 || ^5.7 || ^6.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "IPLib\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Michele Locati", + "email": "mlocati@gmail.com", + "homepage": "https://github.com/mlocati", + "role": "Author" + } + ], + "description": "Handle IPv4, IPv6 addresses and ranges", + "homepage": "https://github.com/mlocati/ip-lib", + "keywords": [ + "IP", + "address", + "addresses", + "ipv4", + "ipv6", + "manage", + "managing", + "matching", + "network", + "networking", + "range", + "subnet" + ], + "funding": [ + { + "url": "https://github.com/sponsors/mlocati", + "type": "github" + }, + { + "url": "https://paypal.me/mlocati", + "type": "other" + } + ], + "time": "2020-12-31T11:30:02+00:00" + }, { "name": "paragonie/random_compat", "version": "v2.0.19", diff --git a/lib/Configuration.php b/lib/Configuration.php index 2a326ca..133059d 100644 --- a/lib/Configuration.php +++ b/lib/Configuration.php @@ -82,6 +82,7 @@ class Configuration 'limit' => 10, 'header' => null, 'dir' => 'data', + 'exemptedIp' => null, ), 'purge' => array( 'limit' => 300, diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php index a16cd0b..31f107f 100644 --- a/lib/Persistence/TrafficLimiter.php +++ b/lib/Persistence/TrafficLimiter.php @@ -1,4 +1,5 @@ getKey('limit', 'traffic')); self::setPath($conf->getKey('dir', 'traffic')); + self::setExemptedIp($conf->getKey('exemptedIp', 'traffic')); + if (($option = $conf->getKey('header', 'traffic')) !== null) { $httpHeader = 'HTTP_' . $option; if (array_key_exists($httpHeader, $_SERVER) && !empty($_SERVER[$httpHeader])) { @@ -99,6 +124,32 @@ class TrafficLimiter extends AbstractPersistence if (self::$_limit < 1) { return true; } + + // Check if $_ipKey is exempted from ratelimiting + if (!is_null(self::$_exemptedIp)) { + $exIp_array = explode(",", self::$_exemptedIp); + foreach ($exIp_array as $ipRange) { + // Match $_ipKey to $ipRange and if it matches it will return with a true + $address = \IPLib\Factory::addressFromString($_SERVER[self::$_ipKey]); + $range = \IPLib\Factory::rangeFromString(trim($ipRange)); + // If $range is null something went wrong (possible invalid ip given in config) + if ($range == null) { + $contained = false; + } else { + // Ip-lib does throws and exception when something goes wrong, if so we want to catch it and set contained to false + try { + $contained = $address->matches($range); + } catch (Exception $e) { + // If something is wrong with matching the ip, we set $contained to false + $contained = false; + } + } + // Matches return true! + if ($contained == true) { + return true; + } + } + } $file = 'traffic_limiter.php'; if (self::_exists($file)) { From b21efd8336c12f39bcf07e5ba9d0387ceee9955d Mon Sep 17 00:00:00 2001 From: rodehoed Date: Tue, 4 May 2021 11:01:46 +0200 Subject: [PATCH 02/14] Code quality --- lib/Persistence/TrafficLimiter.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php index 31f107f..46eddc4 100644 --- a/lib/Persistence/TrafficLimiter.php +++ b/lib/Persistence/TrafficLimiter.php @@ -62,11 +62,11 @@ class TrafficLimiter extends AbstractPersistence } /** - * set a list of ip(ranges) as array + * set a list of ip(ranges) as string * * @access public * @static - * @param array $exemptedIps + * @param string $exemptedIps */ public static function setExemptedIp($exemptedIp) { @@ -139,7 +139,7 @@ class TrafficLimiter extends AbstractPersistence // Ip-lib does throws and exception when something goes wrong, if so we want to catch it and set contained to false try { $contained = $address->matches($range); - } catch (Exception $e) { + } catch (\Exception $e) { // If something is wrong with matching the ip, we set $contained to false $contained = false; } From 805eb288d92be1716214156af50b69dbf88a0afd Mon Sep 17 00:00:00 2001 From: rodehoed Date: Tue, 4 May 2021 11:14:11 +0200 Subject: [PATCH 03/14] QA --- lib/Persistence/TrafficLimiter.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php index 46eddc4..06916c5 100644 --- a/lib/Persistence/TrafficLimiter.php +++ b/lib/Persistence/TrafficLimiter.php @@ -82,7 +82,6 @@ class TrafficLimiter extends AbstractPersistence */ public static function setConfiguration(Configuration $conf) { - self::setLimit($conf->getKey('limit', 'traffic')); self::setPath($conf->getKey('dir', 'traffic')); self::setExemptedIp($conf->getKey('exemptedIp', 'traffic')); @@ -127,11 +126,11 @@ class TrafficLimiter extends AbstractPersistence // Check if $_ipKey is exempted from ratelimiting if (!is_null(self::$_exemptedIp)) { - $exIp_array = explode(",", self::$_exemptedIp); + $exIp_array = explode(',', self::$_exemptedIp); foreach ($exIp_array as $ipRange) { // Match $_ipKey to $ipRange and if it matches it will return with a true $address = \IPLib\Factory::addressFromString($_SERVER[self::$_ipKey]); - $range = \IPLib\Factory::rangeFromString(trim($ipRange)); + $range = \IPLib\Factory::rangeFromString(trim($ipRange)); // If $range is null something went wrong (possible invalid ip given in config) if ($range == null) { $contained = false; From c3ad4a4b4d48946e89785171b83093f3527d5cf5 Mon Sep 17 00:00:00 2001 From: rodehoed Date: Tue, 4 May 2021 11:18:06 +0200 Subject: [PATCH 04/14] QA --- lib/Persistence/TrafficLimiter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php index 06916c5..005fa5f 100644 --- a/lib/Persistence/TrafficLimiter.php +++ b/lib/Persistence/TrafficLimiter.php @@ -30,7 +30,7 @@ class TrafficLimiter extends AbstractPersistence * @var int */ private static $_limit = 10; - + /** * listed ips are exempted from limits, defaults to null * From 4296b43832a59862e7d4abf56e48475ead37c80e Mon Sep 17 00:00:00 2001 From: rodehoed Date: Tue, 4 May 2021 11:19:34 +0200 Subject: [PATCH 05/14] QA --- lib/Persistence/TrafficLimiter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php index 005fa5f..ba0ac33 100644 --- a/lib/Persistence/TrafficLimiter.php +++ b/lib/Persistence/TrafficLimiter.php @@ -123,7 +123,7 @@ class TrafficLimiter extends AbstractPersistence if (self::$_limit < 1) { return true; } - + // Check if $_ipKey is exempted from ratelimiting if (!is_null(self::$_exemptedIp)) { $exIp_array = explode(',', self::$_exemptedIp); From a806a6455eb4591e55ab6e6e90a4d64aa729f95d Mon Sep 17 00:00:00 2001 From: rodehoed Date: Tue, 4 May 2021 11:20:24 +0200 Subject: [PATCH 06/14] QA --- lib/Configuration.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Configuration.php b/lib/Configuration.php index 133059d..da62a1d 100644 --- a/lib/Configuration.php +++ b/lib/Configuration.php @@ -79,9 +79,9 @@ class Configuration 'markdown' => 'Markdown', ), 'traffic' => array( - 'limit' => 10, - 'header' => null, - 'dir' => 'data', + 'limit' => 10, + 'header' => null, + 'dir' => 'data', 'exemptedIp' => null, ), 'purge' => array( From 89bdc92451184420bea4cc6b42282f2875676c93 Mon Sep 17 00:00:00 2001 From: Rodehoed Date: Thu, 6 May 2021 12:13:03 +0200 Subject: [PATCH 07/14] Put the ip-matching function in a private function --- lib/Persistence/TrafficLimiter.php | 54 ++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php index ba0ac33..cbe6adf 100644 --- a/lib/Persistence/TrafficLimiter.php +++ b/lib/Persistence/TrafficLimiter.php @@ -107,6 +107,39 @@ class TrafficLimiter extends AbstractPersistence return hash_hmac($algo, $_SERVER[self::$_ipKey], ServerSalt::get()); } + /** + * Validate $_ipKey against configured ipranges. If matched ratelimiter will ignore ip + * + * @access private + * @static + * @param string $algo + * @return string + */ + private static function matchIp($ipRange = null) + { + // Match $_ipKey to $ipRange and if it matches it will return with a true + $address = \IPLib\Factory::addressFromString($_SERVER[self::$_ipKey]); + $range = \IPLib\Factory::rangeFromString(trim($ipRange)); + // If $range is null something went wrong (possible invalid ip given in config) + if ($range == null) { + return false; + } else { + // Ip-lib does throws and exception when something goes wrong, if so we want to catch it and set contained to false + try { + $contained = $address->matches($range); + } catch (\Exception $e) { + // If something is wrong with matching the ip, we set $contained to false + return false; + } + } + // Matches return true! + if ($contained === true) { + return true; + } else { + return false; + } + } + /** * traffic limiter * @@ -123,28 +156,13 @@ class TrafficLimiter extends AbstractPersistence if (self::$_limit < 1) { return true; } - + error_reporting(-1); // Check if $_ipKey is exempted from ratelimiting if (!is_null(self::$_exemptedIp)) { $exIp_array = explode(',', self::$_exemptedIp); foreach ($exIp_array as $ipRange) { - // Match $_ipKey to $ipRange and if it matches it will return with a true - $address = \IPLib\Factory::addressFromString($_SERVER[self::$_ipKey]); - $range = \IPLib\Factory::rangeFromString(trim($ipRange)); - // If $range is null something went wrong (possible invalid ip given in config) - if ($range == null) { - $contained = false; - } else { - // Ip-lib does throws and exception when something goes wrong, if so we want to catch it and set contained to false - try { - $contained = $address->matches($range); - } catch (\Exception $e) { - // If something is wrong with matching the ip, we set $contained to false - $contained = false; - } - } - // Matches return true! - if ($contained == true) { + if (self::matchIp($ipRange) === true) + { return true; } } From 502bb5fa15b12a232c720bf787ef45f898196934 Mon Sep 17 00:00:00 2001 From: Rodehoed Date: Thu, 6 May 2021 12:18:44 +0200 Subject: [PATCH 08/14] Put the ip-matching function in a private function --- lib/Persistence/TrafficLimiter.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php index cbe6adf..3975540 100644 --- a/lib/Persistence/TrafficLimiter.php +++ b/lib/Persistence/TrafficLimiter.php @@ -112,8 +112,8 @@ class TrafficLimiter extends AbstractPersistence * * @access private * @static - * @param string $algo - * @return string + * @param string $ipRange + * @return bool */ private static function matchIp($ipRange = null) { @@ -156,13 +156,12 @@ class TrafficLimiter extends AbstractPersistence if (self::$_limit < 1) { return true; } - error_reporting(-1); + // Check if $_ipKey is exempted from ratelimiting if (!is_null(self::$_exemptedIp)) { $exIp_array = explode(',', self::$_exemptedIp); foreach ($exIp_array as $ipRange) { - if (self::matchIp($ipRange) === true) - { + if (self::matchIp($ipRange) === true) { return true; } } From 5812a6bb68189415866f4ab4728d2497de7d5795 Mon Sep 17 00:00:00 2001 From: rodehoed <6515395+rodehoed@users.noreply.github.com> Date: Wed, 19 May 2021 08:47:35 +0200 Subject: [PATCH 09/14] Optimized the canPass() functions --- lib/Persistence/TrafficLimiter.php | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php index 3975540..34584b5 100644 --- a/lib/Persistence/TrafficLimiter.php +++ b/lib/Persistence/TrafficLimiter.php @@ -120,24 +120,18 @@ class TrafficLimiter extends AbstractPersistence // Match $_ipKey to $ipRange and if it matches it will return with a true $address = \IPLib\Factory::addressFromString($_SERVER[self::$_ipKey]); $range = \IPLib\Factory::rangeFromString(trim($ipRange)); - // If $range is null something went wrong (possible invalid ip given in config) - if ($range == null) { - return false; - } else { - // Ip-lib does throws and exception when something goes wrong, if so we want to catch it and set contained to false - try { - $contained = $address->matches($range); - } catch (\Exception $e) { - // If something is wrong with matching the ip, we set $contained to false - return false; - } - } - // Matches return true! - if ($contained === true) { - return true; - } else { - return false; + + // If $range is null something went wrong (possible invalid ip given in config). It's here becaue matches($range) does not accepts null vallue + if ($range == null) return false; + + // Ip-lib does throws and exception when something goes wrong, if so we want to catch it and set contained to false + try { + return $address->matches($range); + } catch (\Exception $e) { + // If something is wrong with matching the ip, we do nothing } + + return false; } /** From af5a14afc31474572e076c498cba47cb418a35de Mon Sep 17 00:00:00 2001 From: rodehoed <6515395+rodehoed@users.noreply.github.com> Date: Wed, 19 May 2021 09:01:45 +0200 Subject: [PATCH 10/14] Optimized the canPass() functions --- lib/Persistence/TrafficLimiter.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php index 34584b5..af6e497 100644 --- a/lib/Persistence/TrafficLimiter.php +++ b/lib/Persistence/TrafficLimiter.php @@ -122,7 +122,9 @@ class TrafficLimiter extends AbstractPersistence $range = \IPLib\Factory::rangeFromString(trim($ipRange)); // If $range is null something went wrong (possible invalid ip given in config). It's here becaue matches($range) does not accepts null vallue - if ($range == null) return false; + if ($range == null) { + return false; + } // Ip-lib does throws and exception when something goes wrong, if so we want to catch it and set contained to false try { From 89f6f0051dd56b7a9390e70df03271fe3d1f1353 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 22 May 2021 09:21:01 +0200 Subject: [PATCH 11/14] imported mlocati/ip-lib version 1.14.0 --- composer.json | 4 +- composer.lock | 12 +- vendor/composer/autoload_classmap.php | 14 + vendor/composer/autoload_psr4.php | 1 + vendor/composer/autoload_static.php | 19 + vendor/mlocati/ip-lib/ip-lib.php | 13 + .../ip-lib/src/Address/AddressInterface.php | 125 ++++ .../ip-lib/src/Address/AssignedRange.php | 138 +++++ vendor/mlocati/ip-lib/src/Address/IPv4.php | 465 ++++++++++++++ vendor/mlocati/ip-lib/src/Address/IPv6.php | 568 ++++++++++++++++++ vendor/mlocati/ip-lib/src/Address/Type.php | 42 ++ vendor/mlocati/ip-lib/src/Factory.php | 205 +++++++ .../ip-lib/src/Range/AbstractRange.php | 95 +++ vendor/mlocati/ip-lib/src/Range/Pattern.php | 275 +++++++++ .../ip-lib/src/Range/RangeInterface.php | 120 ++++ vendor/mlocati/ip-lib/src/Range/Single.php | 226 +++++++ vendor/mlocati/ip-lib/src/Range/Subnet.php | 305 ++++++++++ vendor/mlocati/ip-lib/src/Range/Type.php | 150 +++++ .../mlocati/ip-lib/src/Service/BinaryMath.php | 118 ++++ .../Service/RangesFromBounradyCalculator.php | 161 +++++ 20 files changed, 3048 insertions(+), 8 deletions(-) create mode 100644 vendor/mlocati/ip-lib/ip-lib.php create mode 100644 vendor/mlocati/ip-lib/src/Address/AddressInterface.php create mode 100644 vendor/mlocati/ip-lib/src/Address/AssignedRange.php create mode 100644 vendor/mlocati/ip-lib/src/Address/IPv4.php create mode 100644 vendor/mlocati/ip-lib/src/Address/IPv6.php create mode 100644 vendor/mlocati/ip-lib/src/Address/Type.php create mode 100644 vendor/mlocati/ip-lib/src/Factory.php create mode 100644 vendor/mlocati/ip-lib/src/Range/AbstractRange.php create mode 100644 vendor/mlocati/ip-lib/src/Range/Pattern.php create mode 100644 vendor/mlocati/ip-lib/src/Range/RangeInterface.php create mode 100644 vendor/mlocati/ip-lib/src/Range/Single.php create mode 100644 vendor/mlocati/ip-lib/src/Range/Subnet.php create mode 100644 vendor/mlocati/ip-lib/src/Range/Type.php create mode 100644 vendor/mlocati/ip-lib/src/Service/BinaryMath.php create mode 100644 vendor/mlocati/ip-lib/src/Service/RangesFromBounradyCalculator.php diff --git a/composer.json b/composer.json index ff79af9..98aaedd 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,7 @@ "php" : "^5.6.0 || ^7.0 || ^8.0", "paragonie/random_compat" : "2.0.19", "yzalis/identicon" : "2.0.0", - "mlocati/ip-lib": "^1.14" + "mlocati/ip-lib" : "1.14.0" }, "require-dev" : { "phpunit/phpunit" : "^4.6 || ^5.0" @@ -40,4 +40,4 @@ "config" : { "autoloader-suffix" : "DontChange" } -} +} \ No newline at end of file diff --git a/composer.lock b/composer.lock index 66a9c8a..20b0732 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8138b0e4bb3fcaab9ca4c02bbe35d891", + "content-hash": "c3aa7487ba976536cd3db9a60473ce67", "packages": [ { "name": "mlocati/ip-lib", @@ -1495,16 +1495,16 @@ }, { "name": "symfony/yaml", - "version": "v4.4.21", + "version": "v4.4.24", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "3871c720871029f008928244e56cf43497da7e9d" + "reference": "8b6d1b97521e2f125039b3fcb4747584c6dfa0ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/3871c720871029f008928244e56cf43497da7e9d", - "reference": "3871c720871029f008928244e56cf43497da7e9d", + "url": "https://api.github.com/repos/symfony/yaml/zipball/8b6d1b97521e2f125039b3fcb4747584c6dfa0ef", + "reference": "8b6d1b97521e2f125039b3fcb4747584c6dfa0ef", "shasum": "" }, "require": { @@ -1559,7 +1559,7 @@ "type": "tidelift" } ], - "time": "2021-03-05T17:58:50+00:00" + "time": "2021-05-16T09:52:47+00:00" }, { "name": "webmozart/assert", diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index b98b672..81358f5 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -6,6 +6,20 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( + 'IPLib\\Address\\AddressInterface' => $vendorDir . '/mlocati/ip-lib/src/Address/AddressInterface.php', + 'IPLib\\Address\\AssignedRange' => $vendorDir . '/mlocati/ip-lib/src/Address/AssignedRange.php', + 'IPLib\\Address\\IPv4' => $vendorDir . '/mlocati/ip-lib/src/Address/IPv4.php', + 'IPLib\\Address\\IPv6' => $vendorDir . '/mlocati/ip-lib/src/Address/IPv6.php', + 'IPLib\\Address\\Type' => $vendorDir . '/mlocati/ip-lib/src/Address/Type.php', + 'IPLib\\Factory' => $vendorDir . '/mlocati/ip-lib/src/Factory.php', + 'IPLib\\Range\\AbstractRange' => $vendorDir . '/mlocati/ip-lib/src/Range/AbstractRange.php', + 'IPLib\\Range\\Pattern' => $vendorDir . '/mlocati/ip-lib/src/Range/Pattern.php', + 'IPLib\\Range\\RangeInterface' => $vendorDir . '/mlocati/ip-lib/src/Range/RangeInterface.php', + 'IPLib\\Range\\Single' => $vendorDir . '/mlocati/ip-lib/src/Range/Single.php', + 'IPLib\\Range\\Subnet' => $vendorDir . '/mlocati/ip-lib/src/Range/Subnet.php', + 'IPLib\\Range\\Type' => $vendorDir . '/mlocati/ip-lib/src/Range/Type.php', + 'IPLib\\Service\\BinaryMath' => $vendorDir . '/mlocati/ip-lib/src/Service/BinaryMath.php', + 'IPLib\\Service\\RangesFromBounradyCalculator' => $vendorDir . '/mlocati/ip-lib/src/Service/RangesFromBounradyCalculator.php', 'Identicon\\Generator\\BaseGenerator' => $vendorDir . '/yzalis/identicon/src/Identicon/Generator/BaseGenerator.php', 'Identicon\\Generator\\GdGenerator' => $vendorDir . '/yzalis/identicon/src/Identicon/Generator/GdGenerator.php', 'Identicon\\Generator\\GeneratorInterface' => $vendorDir . '/yzalis/identicon/src/Identicon/Generator/GeneratorInterface.php', diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index 313097d..9bd3702 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -8,4 +8,5 @@ $baseDir = dirname($vendorDir); return array( 'PrivateBin\\' => array($baseDir . '/lib'), 'Identicon\\' => array($vendorDir . '/yzalis/identicon/src/Identicon'), + 'IPLib\\' => array($vendorDir . '/mlocati/ip-lib/src'), ); diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 604d6bb..9197c94 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -18,6 +18,7 @@ class ComposerStaticInitDontChange 'I' => array ( 'Identicon\\' => 10, + 'IPLib\\' => 6, ), ); @@ -30,9 +31,27 @@ class ComposerStaticInitDontChange array ( 0 => __DIR__ . '/..' . '/yzalis/identicon/src/Identicon', ), + 'IPLib\\' => + array ( + 0 => __DIR__ . '/..' . '/mlocati/ip-lib/src', + ), ); public static $classMap = array ( + 'IPLib\\Address\\AddressInterface' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Address/AddressInterface.php', + 'IPLib\\Address\\AssignedRange' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Address/AssignedRange.php', + 'IPLib\\Address\\IPv4' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Address/IPv4.php', + 'IPLib\\Address\\IPv6' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Address/IPv6.php', + 'IPLib\\Address\\Type' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Address/Type.php', + 'IPLib\\Factory' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Factory.php', + 'IPLib\\Range\\AbstractRange' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Range/AbstractRange.php', + 'IPLib\\Range\\Pattern' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Range/Pattern.php', + 'IPLib\\Range\\RangeInterface' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Range/RangeInterface.php', + 'IPLib\\Range\\Single' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Range/Single.php', + 'IPLib\\Range\\Subnet' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Range/Subnet.php', + 'IPLib\\Range\\Type' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Range/Type.php', + 'IPLib\\Service\\BinaryMath' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Service/BinaryMath.php', + 'IPLib\\Service\\RangesFromBounradyCalculator' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Service/RangesFromBounradyCalculator.php', 'Identicon\\Generator\\BaseGenerator' => __DIR__ . '/..' . '/yzalis/identicon/src/Identicon/Generator/BaseGenerator.php', 'Identicon\\Generator\\GdGenerator' => __DIR__ . '/..' . '/yzalis/identicon/src/Identicon/Generator/GdGenerator.php', 'Identicon\\Generator\\GeneratorInterface' => __DIR__ . '/..' . '/yzalis/identicon/src/Identicon/Generator/GeneratorInterface.php', diff --git a/vendor/mlocati/ip-lib/ip-lib.php b/vendor/mlocati/ip-lib/ip-lib.php new file mode 100644 index 0000000..c34d0b4 --- /dev/null +++ b/vendor/mlocati/ip-lib/ip-lib.php @@ -0,0 +1,13 @@ +range = $range; + $this->type = $type; + $this->exceptions = $exceptions; + } + + /** + * Get the range definition. + * + * @return \IPLib\Range\RangeInterface + */ + public function getRange() + { + return $this->range; + } + + /** + * Get the range type. + * + * @return int one of the \IPLib\Range\Type::T_ constants + */ + public function getType() + { + return $this->type; + } + + /** + * Get the list of exceptions for this range type. + * + * @return \IPLib\Address\AssignedRange[] + */ + public function getExceptions() + { + return $this->exceptions; + } + + /** + * Get the assigned type for a specific address. + * + * @param \IPLib\Address\AddressInterface $address + * + * @return int|null return NULL of the address is outside this address; a \IPLib\Range\Type::T_ constant otherwise + */ + public function getAddressType(AddressInterface $address) + { + $result = null; + if ($this->range->contains($address)) { + foreach ($this->exceptions as $exception) { + $result = $exception->getAddressType($address); + if ($result !== null) { + break; + } + } + if ($result === null) { + $result = $this->type; + } + } + + return $result; + } + + /** + * Get the assigned type for a specific address range. + * + * @param \IPLib\Range\RangeInterface $range + * + * @return int|false|null return NULL of the range is fully outside this range; false if it's partly crosses this range (or it contains mixed types); a \IPLib\Range\Type::T_ constant otherwise + */ + public function getRangeType(RangeInterface $range) + { + $myStart = $this->range->getComparableStartString(); + $rangeEnd = $range->getComparableEndString(); + if ($myStart > $rangeEnd) { + $result = null; + } else { + $myEnd = $this->range->getComparableEndString(); + $rangeStart = $range->getComparableStartString(); + if ($myEnd < $rangeStart) { + $result = null; + } elseif ($rangeStart < $myStart || $rangeEnd > $myEnd) { + $result = false; + } else { + $result = null; + foreach ($this->exceptions as $exception) { + $result = $exception->getRangeType($range); + if ($result !== null) { + break; + } + } + if ($result === null) { + $result = $this->getType(); + } + } + } + + return $result; + } +} diff --git a/vendor/mlocati/ip-lib/src/Address/IPv4.php b/vendor/mlocati/ip-lib/src/Address/IPv4.php new file mode 100644 index 0000000..d31338e --- /dev/null +++ b/vendor/mlocati/ip-lib/src/Address/IPv4.php @@ -0,0 +1,465 @@ +address = $address; + $this->bytes = null; + $this->rangeType = null; + $this->comparableString = null; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::__toString() + */ + public function __toString() + { + return $this->address; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::getNumberOfBits() + */ + public static function getNumberOfBits() + { + return 32; + } + + /** + * Parse a string and returns an IPv4 instance if the string is valid, or null otherwise. + * + * @param string|mixed $address the address to parse + * @param bool $mayIncludePort set to false to avoid parsing addresses with ports + * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses + * + * @return static|null + */ + public static function fromString($address, $mayIncludePort = true, $supportNonDecimalIPv4 = false) + { + if (!is_string($address) || !strpos($address, '.')) { + return null; + } + $rxChunk = '0?[0-9]{1,3}'; + if ($supportNonDecimalIPv4) { + $rxChunk = "(?:0[Xx]0*[0-9A-Fa-f]{1,2})|(?:{$rxChunk})"; + } + $rx = "0*?({$rxChunk})\.0*?({$rxChunk})\.0*?({$rxChunk})\.0*?({$rxChunk})"; + if ($mayIncludePort) { + $rx .= '(?::\d+)?'; + } + $matches = null; + if (!preg_match('/^' . $rx . '$/', $address, $matches)) { + return null; + } + $nums = array(); + for ($i = 1; $i <= 4; $i++) { + $s = $matches[$i]; + if ($supportNonDecimalIPv4) { + if (stripos($s, '0x') === 0) { + $n = hexdec(substr($s, 2)); + } elseif ($s[0] === '0') { + if (!preg_match('/^[0-7]+$/', $s)) { + return null; + } + $n = octdec(substr($s, 1)); + } else { + $n = (int) $s; + } + } else { + $n = (int) $s; + } + if ($n < 0 || $n > 255) { + return null; + } + $nums[] = (string) $n; + } + + return new static(implode('.', $nums)); + } + + /** + * Parse an array of bytes and returns an IPv4 instance if the array is valid, or null otherwise. + * + * @param int[]|array $bytes + * + * @return static|null + */ + public static function fromBytes(array $bytes) + { + $result = null; + if (count($bytes) === 4) { + $chunks = array_map( + function ($byte) { + return (is_int($byte) && $byte >= 0 && $byte <= 255) ? (string) $byte : false; + }, + $bytes + ); + if (in_array(false, $chunks, true) === false) { + $result = new static(implode('.', $chunks)); + } + } + + return $result; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::toString() + */ + public function toString($long = false) + { + if ($long) { + return $this->getComparableString(); + } + + return $this->address; + } + + /** + * Get the octal representation of this IP address. + * + * @param bool $long + * + * @return string + * + * @example if $long == false: if the decimal representation is '0.7.8.255': '0.7.010.0377' + * @example if $long == true: if the decimal representation is '0.7.8.255': '0000.0007.0010.0377' + */ + public function toOctal($long = false) + { + $chunks = array(); + foreach ($this->getBytes() as $byte) { + if ($long) { + $chunks[] = sprintf('%04o', $byte); + } else { + $chunks[] = '0' . decoct($byte); + } + } + + return implode('.', $chunks); + } + + /** + * Get the hexadecimal representation of this IP address. + * + * @param bool $long + * + * @return string + * + * @example if $long == false: if the decimal representation is '0.9.10.255': '0.9.0xa.0xff' + * @example if $long == true: if the decimal representation is '0.9.10.255': '0x00.0x09.0x0a.0xff' + */ + public function toHexadecimal($long = false) + { + $chunks = array(); + foreach ($this->getBytes() as $byte) { + if ($long) { + $chunks[] = sprintf('0x%02x', $byte); + } else { + $chunks[] = '0x' . dechex($byte); + } + } + + return implode('.', $chunks); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::getBytes() + */ + public function getBytes() + { + if ($this->bytes === null) { + $this->bytes = array_map( + function ($chunk) { + return (int) $chunk; + }, + explode('.', $this->address) + ); + } + + return $this->bytes; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::getBits() + */ + public function getBits() + { + $parts = array(); + foreach ($this->getBytes() as $byte) { + $parts[] = sprintf('%08b', $byte); + } + + return implode('', $parts); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::getAddressType() + */ + public function getAddressType() + { + return Type::T_IPv4; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::getDefaultReservedRangeType() + */ + public static function getDefaultReservedRangeType() + { + return RangeType::T_PUBLIC; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::getReservedRanges() + */ + public static function getReservedRanges() + { + if (self::$reservedRanges === null) { + $reservedRanges = array(); + foreach (array( + // RFC 5735 + '0.0.0.0/8' => array(RangeType::T_THISNETWORK, array('0.0.0.0/32' => RangeType::T_UNSPECIFIED)), + // RFC 5735 + '10.0.0.0/8' => array(RangeType::T_PRIVATENETWORK), + // RFC 6598 + '100.64.0.0/10' => array(RangeType::T_CGNAT), + // RFC 5735 + '127.0.0.0/8' => array(RangeType::T_LOOPBACK), + // RFC 5735 + '169.254.0.0/16' => array(RangeType::T_LINKLOCAL), + // RFC 5735 + '172.16.0.0/12' => array(RangeType::T_PRIVATENETWORK), + // RFC 5735 + '192.0.0.0/24' => array(RangeType::T_RESERVED), + // RFC 5735 + '192.0.2.0/24' => array(RangeType::T_RESERVED), + // RFC 5735 + '192.88.99.0/24' => array(RangeType::T_ANYCASTRELAY), + // RFC 5735 + '192.168.0.0/16' => array(RangeType::T_PRIVATENETWORK), + // RFC 5735 + '198.18.0.0/15' => array(RangeType::T_RESERVED), + // RFC 5735 + '198.51.100.0/24' => array(RangeType::T_RESERVED), + // RFC 5735 + '203.0.113.0/24' => array(RangeType::T_RESERVED), + // RFC 5735 + '224.0.0.0/4' => array(RangeType::T_MULTICAST), + // RFC 5735 + '240.0.0.0/4' => array(RangeType::T_RESERVED, array('255.255.255.255/32' => RangeType::T_LIMITEDBROADCAST)), + ) as $range => $data) { + $exceptions = array(); + if (isset($data[1])) { + foreach ($data[1] as $exceptionRange => $exceptionType) { + $exceptions[] = new AssignedRange(Subnet::fromString($exceptionRange), $exceptionType); + } + } + $reservedRanges[] = new AssignedRange(Subnet::fromString($range), $data[0], $exceptions); + } + self::$reservedRanges = $reservedRanges; + } + + return self::$reservedRanges; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::getRangeType() + */ + public function getRangeType() + { + if ($this->rangeType === null) { + $rangeType = null; + foreach (static::getReservedRanges() as $reservedRange) { + $rangeType = $reservedRange->getAddressType($this); + if ($rangeType !== null) { + break; + } + } + $this->rangeType = $rangeType === null ? static::getDefaultReservedRangeType() : $rangeType; + } + + return $this->rangeType; + } + + /** + * Create an IPv6 representation of this address (in 6to4 notation). + * + * @return \IPLib\Address\IPv6 + */ + public function toIPv6() + { + $myBytes = $this->getBytes(); + + return IPv6::fromString('2002:' . sprintf('%02x', $myBytes[0]) . sprintf('%02x', $myBytes[1]) . ':' . sprintf('%02x', $myBytes[2]) . sprintf('%02x', $myBytes[3]) . '::'); + } + + /** + * Create an IPv6 representation of this address (in IPv6 IPv4-mapped notation). + * + * @return \IPLib\Address\IPv6 + */ + public function toIPv6IPv4Mapped() + { + return IPv6::fromBytes(array_merge(array(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff), $this->getBytes())); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::getComparableString() + */ + public function getComparableString() + { + if ($this->comparableString === null) { + $chunks = array(); + foreach ($this->getBytes() as $byte) { + $chunks[] = sprintf('%03d', $byte); + } + $this->comparableString = implode('.', $chunks); + } + + return $this->comparableString; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::matches() + */ + public function matches(RangeInterface $range) + { + return $range->contains($this); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::getNextAddress() + */ + public function getNextAddress() + { + $overflow = false; + $bytes = $this->getBytes(); + for ($i = count($bytes) - 1; $i >= 0; $i--) { + if ($bytes[$i] === 255) { + if ($i === 0) { + $overflow = true; + break; + } + $bytes[$i] = 0; + } else { + $bytes[$i]++; + break; + } + } + + return $overflow ? null : static::fromBytes($bytes); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::getPreviousAddress() + */ + public function getPreviousAddress() + { + $overflow = false; + $bytes = $this->getBytes(); + for ($i = count($bytes) - 1; $i >= 0; $i--) { + if ($bytes[$i] === 0) { + if ($i === 0) { + $overflow = true; + break; + } + $bytes[$i] = 255; + } else { + $bytes[$i]--; + break; + } + } + + return $overflow ? null : static::fromBytes($bytes); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::getReverseDNSLookupName() + */ + public function getReverseDNSLookupName() + { + return implode( + '.', + array_reverse($this->getBytes()) + ) . '.in-addr.arpa'; + } +} diff --git a/vendor/mlocati/ip-lib/src/Address/IPv6.php b/vendor/mlocati/ip-lib/src/Address/IPv6.php new file mode 100644 index 0000000..3666487 --- /dev/null +++ b/vendor/mlocati/ip-lib/src/Address/IPv6.php @@ -0,0 +1,568 @@ +longAddress = $longAddress; + $this->shortAddress = null; + $this->bytes = null; + $this->words = null; + $this->rangeType = null; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::__toString() + */ + public function __toString() + { + return $this->toString(); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::getNumberOfBits() + */ + public static function getNumberOfBits() + { + return 128; + } + + /** + * Parse a string and returns an IPv6 instance if the string is valid, or null otherwise. + * + * @param string|mixed $address the address to parse + * @param bool $mayIncludePort set to false to avoid parsing addresses with ports + * @param bool $mayIncludeZoneID set to false to avoid parsing addresses with zone IDs (see RFC 4007) + * + * @return static|null + */ + public static function fromString($address, $mayIncludePort = true, $mayIncludeZoneID = true) + { + $result = null; + if (is_string($address) && strpos($address, ':') !== false && strpos($address, ':::') === false) { + $matches = null; + if ($mayIncludePort && $address[0] === '[' && preg_match('/^\[(.+)]:\d+$/', $address, $matches)) { + $address = $matches[1]; + } + if ($mayIncludeZoneID) { + $percentagePos = strpos($address, '%'); + if ($percentagePos > 0) { + $address = substr($address, 0, $percentagePos); + } + } + if (preg_match('/^((?:[0-9a-f]*:+)+)(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/i', $address, $matches)) { + $address6 = static::fromString($matches[1] . '0:0', false); + if ($address6 !== null) { + $address4 = IPv4::fromString($matches[2], false); + if ($address4 !== null) { + $bytes4 = $address4->getBytes(); + $address6->longAddress = substr($address6->longAddress, 0, -9) . sprintf('%02x%02x:%02x%02x', $bytes4[0], $bytes4[1], $bytes4[2], $bytes4[3]); + $result = $address6; + } + } + } else { + if (strpos($address, '::') === false) { + $chunks = explode(':', $address); + } else { + $chunks = array(); + $parts = explode('::', $address); + if (count($parts) === 2) { + $before = ($parts[0] === '') ? array() : explode(':', $parts[0]); + $after = ($parts[1] === '') ? array() : explode(':', $parts[1]); + $missing = 8 - count($before) - count($after); + if ($missing >= 0) { + $chunks = $before; + if ($missing !== 0) { + $chunks = array_merge($chunks, array_fill(0, $missing, '0')); + } + $chunks = array_merge($chunks, $after); + } + } + } + if (count($chunks) === 8) { + $nums = array_map( + function ($chunk) { + return preg_match('/^[0-9A-Fa-f]{1,4}$/', $chunk) ? hexdec($chunk) : false; + }, + $chunks + ); + if (!in_array(false, $nums, true)) { + $longAddress = implode( + ':', + array_map( + function ($num) { + return sprintf('%04x', $num); + }, + $nums + ) + ); + $result = new static($longAddress); + } + } + } + } + + return $result; + } + + /** + * Parse an array of bytes and returns an IPv6 instance if the array is valid, or null otherwise. + * + * @param int[]|array $bytes + * + * @return static|null + */ + public static function fromBytes(array $bytes) + { + $result = null; + if (count($bytes) === 16) { + $address = ''; + for ($i = 0; $i < 16; $i++) { + if ($i !== 0 && $i % 2 === 0) { + $address .= ':'; + } + $byte = $bytes[$i]; + if (is_int($byte) && $byte >= 0 && $byte <= 255) { + $address .= sprintf('%02x', $byte); + } else { + $address = null; + break; + } + } + if ($address !== null) { + $result = new static($address); + } + } + + return $result; + } + + /** + * Parse an array of words and returns an IPv6 instance if the array is valid, or null otherwise. + * + * @param int[]|array $words + * + * @return static|null + */ + public static function fromWords(array $words) + { + $result = null; + if (count($words) === 8) { + $chunks = array(); + for ($i = 0; $i < 8; $i++) { + $word = $words[$i]; + if (is_int($word) && $word >= 0 && $word <= 0xffff) { + $chunks[] = sprintf('%04x', $word); + } else { + $chunks = null; + break; + } + } + if ($chunks !== null) { + $result = new static(implode(':', $chunks)); + } + } + + return $result; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::toString() + */ + public function toString($long = false) + { + if ($long) { + $result = $this->longAddress; + } else { + if ($this->shortAddress === null) { + if (strpos($this->longAddress, '0000:0000:0000:0000:0000:ffff:') === 0) { + $lastBytes = array_slice($this->getBytes(), -4); + $this->shortAddress = '::ffff:' . implode('.', $lastBytes); + } else { + $chunks = array_map( + function ($word) { + return dechex($word); + }, + $this->getWords() + ); + $shortAddress = implode(':', $chunks); + $matches = null; + for ($i = 8; $i > 1; $i--) { + $search = '(?:^|:)' . rtrim(str_repeat('0:', $i), ':') . '(?:$|:)'; + if (preg_match('/^(.*?)' . $search . '(.*)$/', $shortAddress, $matches)) { + $shortAddress = $matches[1] . '::' . $matches[2]; + break; + } + } + $this->shortAddress = $shortAddress; + } + } + $result = $this->shortAddress; + } + + return $result; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::getBytes() + */ + public function getBytes() + { + if ($this->bytes === null) { + $bytes = array(); + foreach ($this->getWords() as $word) { + $bytes[] = $word >> 8; + $bytes[] = $word & 0xff; + } + $this->bytes = $bytes; + } + + return $this->bytes; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::getBits() + */ + public function getBits() + { + $parts = array(); + foreach ($this->getBytes() as $byte) { + $parts[] = sprintf('%08b', $byte); + } + + return implode('', $parts); + } + + /** + * Get the word list of the IP address. + * + * @return int[] + */ + public function getWords() + { + if ($this->words === null) { + $this->words = array_map( + function ($chunk) { + return hexdec($chunk); + }, + explode(':', $this->longAddress) + ); + } + + return $this->words; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::getAddressType() + */ + public function getAddressType() + { + return Type::T_IPv6; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::getDefaultReservedRangeType() + */ + public static function getDefaultReservedRangeType() + { + return RangeType::T_RESERVED; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::getReservedRanges() + */ + public static function getReservedRanges() + { + if (self::$reservedRanges === null) { + $reservedRanges = array(); + foreach (array( + // RFC 4291 + '::/128' => array(RangeType::T_UNSPECIFIED), + // RFC 4291 + '::1/128' => array(RangeType::T_LOOPBACK), + // RFC 4291 + '100::/8' => array(RangeType::T_DISCARD, array('100::/64' => RangeType::T_DISCARDONLY)), + //'2002::/16' => array(RangeType::), + // RFC 4291 + '2000::/3' => array(RangeType::T_PUBLIC), + // RFC 4193 + 'fc00::/7' => array(RangeType::T_PRIVATENETWORK), + // RFC 4291 + 'fe80::/10' => array(RangeType::T_LINKLOCAL_UNICAST), + // RFC 4291 + 'ff00::/8' => array(RangeType::T_MULTICAST), + // RFC 4291 + //'::/8' => array(RangeType::T_RESERVED), + // RFC 4048 + //'200::/7' => array(RangeType::T_RESERVED), + // RFC 4291 + //'400::/6' => array(RangeType::T_RESERVED), + // RFC 4291 + //'800::/5' => array(RangeType::T_RESERVED), + // RFC 4291 + //'1000::/4' => array(RangeType::T_RESERVED), + // RFC 4291 + //'4000::/3' => array(RangeType::T_RESERVED), + // RFC 4291 + //'6000::/3' => array(RangeType::T_RESERVED), + // RFC 4291 + //'8000::/3' => array(RangeType::T_RESERVED), + // RFC 4291 + //'a000::/3' => array(RangeType::T_RESERVED), + // RFC 4291 + //'c000::/3' => array(RangeType::T_RESERVED), + // RFC 4291 + //'e000::/4' => array(RangeType::T_RESERVED), + // RFC 4291 + //'f000::/5' => array(RangeType::T_RESERVED), + // RFC 4291 + //'f800::/6' => array(RangeType::T_RESERVED), + // RFC 4291 + //'fe00::/9' => array(RangeType::T_RESERVED), + // RFC 3879 + //'fec0::/10' => array(RangeType::T_RESERVED), + ) as $range => $data) { + $exceptions = array(); + if (isset($data[1])) { + foreach ($data[1] as $exceptionRange => $exceptionType) { + $exceptions[] = new AssignedRange(Subnet::fromString($exceptionRange), $exceptionType); + } + } + $reservedRanges[] = new AssignedRange(Subnet::fromString($range), $data[0], $exceptions); + } + self::$reservedRanges = $reservedRanges; + } + + return self::$reservedRanges; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::getRangeType() + */ + public function getRangeType() + { + if ($this->rangeType === null) { + $ipv4 = $this->toIPv4(); + if ($ipv4 !== null) { + $this->rangeType = $ipv4->getRangeType(); + } else { + $rangeType = null; + foreach (static::getReservedRanges() as $reservedRange) { + $rangeType = $reservedRange->getAddressType($this); + if ($rangeType !== null) { + break; + } + } + $this->rangeType = $rangeType === null ? static::getDefaultReservedRangeType() : $rangeType; + } + } + + return $this->rangeType; + } + + /** + * Create an IPv4 representation of this address (if possible, otherwise returns null). + * + * @return \IPLib\Address\IPv4|null + */ + public function toIPv4() + { + if (strpos($this->longAddress, '2002:') === 0) { + // 6to4 + return IPv4::fromBytes(array_slice($this->getBytes(), 2, 4)); + } + if (strpos($this->longAddress, '0000:0000:0000:0000:0000:ffff:') === 0) { + // IPv4-mapped IPv6 addresses + return IPv4::fromBytes(array_slice($this->getBytes(), -4)); + } + + return null; + } + + /** + * Render this IPv6 address in the "mixed" IPv6 (first 12 bytes) + IPv4 (last 4 bytes) mixed syntax. + * + * @param bool $ipV6Long render the IPv6 part in "long" format? + * @param bool $ipV4Long render the IPv4 part in "long" format? + * + * @return string + * + * @example '::13.1.68.3' + * @example '0000:0000:0000:0000:0000:0000:13.1.68.3' when $ipV6Long is true + * @example '::013.001.068.003' when $ipV4Long is true + * @example '0000:0000:0000:0000:0000:0000:013.001.068.003' when $ipV6Long and $ipV4Long are true + * + * @see https://tools.ietf.org/html/rfc4291#section-2.2 point 3. + */ + public function toMixedIPv6IPv4String($ipV6Long = false, $ipV4Long = false) + { + $myBytes = $this->getBytes(); + $ipv6Bytes = array_merge(array_slice($myBytes, 0, 12), array(0xff, 0xff, 0xff, 0xff)); + $ipv6String = static::fromBytes($ipv6Bytes)->toString($ipV6Long); + $ipv4Bytes = array_slice($myBytes, 12, 4); + $ipv4String = IPv4::fromBytes($ipv4Bytes)->toString($ipV4Long); + + return preg_replace('/((ffff:ffff)|(\d+(\.\d+){3}))$/i', $ipv4String, $ipv6String); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::getComparableString() + */ + public function getComparableString() + { + return $this->longAddress; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::matches() + */ + public function matches(RangeInterface $range) + { + return $range->contains($this); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::getNextAddress() + */ + public function getNextAddress() + { + $overflow = false; + $words = $this->getWords(); + for ($i = count($words) - 1; $i >= 0; $i--) { + if ($words[$i] === 0xffff) { + if ($i === 0) { + $overflow = true; + break; + } + $words[$i] = 0; + } else { + $words[$i]++; + break; + } + } + + return $overflow ? null : static::fromWords($words); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::getPreviousAddress() + */ + public function getPreviousAddress() + { + $overflow = false; + $words = $this->getWords(); + for ($i = count($words) - 1; $i >= 0; $i--) { + if ($words[$i] === 0) { + if ($i === 0) { + $overflow = true; + break; + } + $words[$i] = 0xffff; + } else { + $words[$i]--; + break; + } + } + + return $overflow ? null : static::fromWords($words); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Address\AddressInterface::getReverseDNSLookupName() + */ + public function getReverseDNSLookupName() + { + return implode( + '.', + array_reverse(str_split(str_replace(':', '', $this->toString(true)), 1)) + ) . '.ip6.arpa'; + } +} diff --git a/vendor/mlocati/ip-lib/src/Address/Type.php b/vendor/mlocati/ip-lib/src/Address/Type.php new file mode 100644 index 0000000..06b0753 --- /dev/null +++ b/vendor/mlocati/ip-lib/src/Address/Type.php @@ -0,0 +1,42 @@ +getNumberOfBits())); + } + $numberOfBits = $from->getNumberOfBits(); + if ($to->getNumberOfBits() !== $numberOfBits) { + return null; + } + $calculator = new RangesFromBounradyCalculator($numberOfBits); + + return $calculator->getRanges($from, $to); + } + + /** + * @param \IPLib\Address\AddressInterface $from + * @param \IPLib\Address\AddressInterface $to + * + * @return \IPLib\Range\RangeInterface|null + */ + protected static function rangeFromBoundaryAddresses(AddressInterface $from = null, AddressInterface $to = null) + { + if ($from === null && $to === null) { + $result = null; + } elseif ($to === null) { + $result = Range\Single::fromAddress($from); + } elseif ($from === null) { + $result = Range\Single::fromAddress($to); + } else { + $result = null; + $addressType = $from->getAddressType(); + if ($addressType === $to->getAddressType()) { + $cmp = strcmp($from->getComparableString(), $to->getComparableString()); + if ($cmp === 0) { + $result = Range\Single::fromAddress($from); + } else { + if ($cmp > 0) { + list($from, $to) = array($to, $from); + } + $fromBytes = $from->getBytes(); + $toBytes = $to->getBytes(); + $numBytes = count($fromBytes); + $sameBits = 0; + for ($byteIndex = 0; $byteIndex < $numBytes; $byteIndex++) { + $fromByte = $fromBytes[$byteIndex]; + $toByte = $toBytes[$byteIndex]; + if ($fromByte === $toByte) { + $sameBits += 8; + } else { + $differentBitsInByte = decbin($fromByte ^ $toByte); + $sameBits += 8 - strlen($differentBitsInByte); + break; + } + } + $result = static::rangeFromString($from->toString(true) . '/' . (string) $sameBits); + } + } + } + + return $result; + } + + /** + * @param string|\IPLib\Address\AddressInterface $from + * @param string|\IPLib\Address\AddressInterface $to + * @param bool $supportNonDecimalIPv4 + * + * @return \IPLib\Address\AddressInterface[]|null[]|false[] + */ + private static function parseBoundaries($from, $to, $supportNonDecimalIPv4 = false) + { + $result = array(); + foreach (array('from', 'to') as $param) { + $value = $$param; + if (!($value instanceof AddressInterface)) { + $value = (string) $value; + if ($value === '') { + $value = null; + } else { + $value = static::addressFromString($value, true, true, $supportNonDecimalIPv4); + if ($value === null) { + $value = false; + } + } + } + $result[] = $value; + } + if ($result[0] && $result[1] && strcmp($result[0]->getComparableString(), $result[1]->getComparableString()) > 0) { + $result = array($result[1], $result[0]); + } + + return $result; + } +} diff --git a/vendor/mlocati/ip-lib/src/Range/AbstractRange.php b/vendor/mlocati/ip-lib/src/Range/AbstractRange.php new file mode 100644 index 0000000..1da94d0 --- /dev/null +++ b/vendor/mlocati/ip-lib/src/Range/AbstractRange.php @@ -0,0 +1,95 @@ +rangeType === null) { + $addressType = $this->getAddressType(); + if ($addressType === AddressType::T_IPv6 && Subnet::get6to4()->containsRange($this)) { + $this->rangeType = Factory::rangeFromBoundaries($this->fromAddress->toIPv4(), $this->toAddress->toIPv4())->getRangeType(); + } else { + switch ($addressType) { + case AddressType::T_IPv4: + $defaultType = IPv4::getDefaultReservedRangeType(); + $reservedRanges = IPv4::getReservedRanges(); + break; + case AddressType::T_IPv6: + $defaultType = IPv6::getDefaultReservedRangeType(); + $reservedRanges = IPv6::getReservedRanges(); + break; + default: + throw new \Exception('@todo'); // @codeCoverageIgnore + } + $rangeType = null; + foreach ($reservedRanges as $reservedRange) { + $rangeType = $reservedRange->getRangeType($this); + if ($rangeType !== null) { + break; + } + } + $this->rangeType = $rangeType === null ? $defaultType : $rangeType; + } + } + + return $this->rangeType === false ? null : $this->rangeType; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::contains() + */ + public function contains(AddressInterface $address) + { + $result = false; + if ($address->getAddressType() === $this->getAddressType()) { + $cmp = $address->getComparableString(); + $from = $this->getComparableStartString(); + if ($cmp >= $from) { + $to = $this->getComparableEndString(); + if ($cmp <= $to) { + $result = true; + } + } + } + + return $result; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::containsRange() + */ + public function containsRange(RangeInterface $range) + { + $result = false; + if ($range->getAddressType() === $this->getAddressType()) { + $myStart = $this->getComparableStartString(); + $itsStart = $range->getComparableStartString(); + if ($itsStart >= $myStart) { + $myEnd = $this->getComparableEndString(); + $itsEnd = $range->getComparableEndString(); + if ($itsEnd <= $myEnd) { + $result = true; + } + } + } + + return $result; + } +} diff --git a/vendor/mlocati/ip-lib/src/Range/Pattern.php b/vendor/mlocati/ip-lib/src/Range/Pattern.php new file mode 100644 index 0000000..fc19d1e --- /dev/null +++ b/vendor/mlocati/ip-lib/src/Range/Pattern.php @@ -0,0 +1,275 @@ +fromAddress = $fromAddress; + $this->toAddress = $toAddress; + $this->asterisksCount = $asterisksCount; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::__toString() + */ + public function __toString() + { + return $this->toString(); + } + + /** + * Try get the range instance starting from its string representation. + * + * @param string|mixed $range + * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses + * + * @return static|null + */ + public static function fromString($range, $supportNonDecimalIPv4 = false) + { + if (!is_string($range) || strpos($range, '*') === false) { + return null; + } + if ($range === '*.*.*.*') { + return new static(IPv4::fromString('0.0.0.0'), IPv4::fromString('255.255.255.255'), 4); + } + if ($range === '*:*:*:*:*:*:*:*') { + return new static(IPv6::fromString('::'), IPv6::fromString('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'), 8); + } + $matches = null; + if (strpos($range, '.') !== false && preg_match('/^[^*]+((?:\.\*)+)$/', $range, $matches)) { + $asterisksCount = strlen($matches[1]) >> 1; + if ($asterisksCount > 0) { + $missingDots = 3 - substr_count($range, '.'); + if ($missingDots > 0) { + $range .= str_repeat('.*', $missingDots); + $asterisksCount += $missingDots; + } + } + $fromAddress = IPv4::fromString(str_replace('*', '0', $range), true, $supportNonDecimalIPv4); + if ($fromAddress === null) { + return null; + } + $fixedBytes = array_slice($fromAddress->getBytes(), 0, -$asterisksCount); + $otherBytes = array_fill(0, $asterisksCount, 255); + $toAddress = IPv4::fromBytes(array_merge($fixedBytes, $otherBytes)); + + return new static($fromAddress, $toAddress, $asterisksCount); + } + if (strpos($range, ':') !== false && preg_match('/^[^*]+((?::\*)+)$/', $range, $matches)) { + $asterisksCount = strlen($matches[1]) >> 1; + $fromAddress = IPv6::fromString(str_replace('*', '0', $range)); + if ($fromAddress === null) { + return null; + } + $fixedWords = array_slice($fromAddress->getWords(), 0, -$asterisksCount); + $otherWords = array_fill(0, $asterisksCount, 0xffff); + $toAddress = IPv6::fromWords(array_merge($fixedWords, $otherWords)); + + return new static($fromAddress, $toAddress, $asterisksCount); + } + + return null; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::toString() + */ + public function toString($long = false) + { + if ($this->asterisksCount === 0) { + return $this->fromAddress->toString($long); + } + switch (true) { + case $this->fromAddress instanceof \IPLib\Address\IPv4: + $chunks = explode('.', $this->fromAddress->toString()); + $chunks = array_slice($chunks, 0, -$this->asterisksCount); + $chunks = array_pad($chunks, 4, '*'); + $result = implode('.', $chunks); + break; + case $this->fromAddress instanceof \IPLib\Address\IPv6: + if ($long) { + $chunks = explode(':', $this->fromAddress->toString(true)); + $chunks = array_slice($chunks, 0, -$this->asterisksCount); + $chunks = array_pad($chunks, 8, '*'); + $result = implode(':', $chunks); + } elseif ($this->asterisksCount === 8) { + $result = '*:*:*:*:*:*:*:*'; + } else { + $bytes = $this->toAddress->getBytes(); + $bytes = array_slice($bytes, 0, -$this->asterisksCount * 2); + $bytes = array_pad($bytes, 16, 1); + $address = IPv6::fromBytes($bytes); + $before = substr($address->toString(false), 0, -strlen(':101') * $this->asterisksCount); + $result = $before . str_repeat(':*', $this->asterisksCount); + } + break; + default: + throw new \Exception('@todo'); // @codeCoverageIgnore + } + + return $result; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::getAddressType() + */ + public function getAddressType() + { + return $this->fromAddress->getAddressType(); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::getStartAddress() + */ + public function getStartAddress() + { + return $this->fromAddress; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::getEndAddress() + */ + public function getEndAddress() + { + return $this->toAddress; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::getComparableStartString() + */ + public function getComparableStartString() + { + return $this->fromAddress->getComparableString(); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::getComparableEndString() + */ + public function getComparableEndString() + { + return $this->toAddress->getComparableString(); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::asSubnet() + */ + public function asSubnet() + { + switch ($this->getAddressType()) { + case AddressType::T_IPv4: + return new Subnet($this->getStartAddress(), $this->getEndAddress(), 8 * (4 - $this->asterisksCount)); + case AddressType::T_IPv6: + return new Subnet($this->getStartAddress(), $this->getEndAddress(), 16 * (8 - $this->asterisksCount)); + } + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::asPattern() + */ + public function asPattern() + { + return $this; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::getSubnetMask() + */ + public function getSubnetMask() + { + if ($this->getAddressType() !== AddressType::T_IPv4) { + return null; + } + switch ($this->asterisksCount) { + case 0: + $bytes = array(255, 255, 255, 255); + break; + case 4: + $bytes = array(0, 0, 0, 0); + break; + default: + $bytes = array_pad(array_fill(0, 4 - $this->asterisksCount, 255), 4, 0); + break; + } + + return IPv4::fromBytes($bytes); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::getReverseDNSLookupName() + */ + public function getReverseDNSLookupName() + { + return $this->asterisksCount === 0 ? array($this->getStartAddress()->getReverseDNSLookupName()) : $this->asSubnet()->getReverseDNSLookupName(); + } +} diff --git a/vendor/mlocati/ip-lib/src/Range/RangeInterface.php b/vendor/mlocati/ip-lib/src/Range/RangeInterface.php new file mode 100644 index 0000000..468df3e --- /dev/null +++ b/vendor/mlocati/ip-lib/src/Range/RangeInterface.php @@ -0,0 +1,120 @@ +address = $address; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::__toString() + */ + public function __toString() + { + return $this->address->__toString(); + } + + /** + * Try get the range instance starting from its string representation. + * + * @param string|mixed $range + * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses + * + * @return static|null + */ + public static function fromString($range, $supportNonDecimalIPv4 = false) + { + $result = null; + $address = Factory::addressFromString($range, true, true, $supportNonDecimalIPv4); + if ($address !== null) { + $result = new static($address); + } + + return $result; + } + + /** + * Create the range instance starting from an address instance. + * + * @param \IPLib\Address\AddressInterface $address + * + * @return static + */ + public static function fromAddress(AddressInterface $address) + { + return new static($address); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::toString() + */ + public function toString($long = false) + { + return $this->address->toString($long); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::getAddressType() + */ + public function getAddressType() + { + return $this->address->getAddressType(); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::getRangeType() + */ + public function getRangeType() + { + return $this->address->getRangeType(); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::contains() + */ + public function contains(AddressInterface $address) + { + $result = false; + if ($address->getAddressType() === $this->getAddressType()) { + if ($address->toString(false) === $this->address->toString(false)) { + $result = true; + } + } + + return $result; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::containsRange() + */ + public function containsRange(RangeInterface $range) + { + $result = false; + if ($range->getAddressType() === $this->getAddressType()) { + if ($range->toString(false) === $this->toString(false)) { + $result = true; + } + } + + return $result; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::getStartAddress() + */ + public function getStartAddress() + { + return $this->address; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::getEndAddress() + */ + public function getEndAddress() + { + return $this->address; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::getComparableStartString() + */ + public function getComparableStartString() + { + return $this->address->getComparableString(); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::getComparableEndString() + */ + public function getComparableEndString() + { + return $this->address->getComparableString(); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::asSubnet() + */ + public function asSubnet() + { + $networkPrefixes = array( + AddressType::T_IPv4 => 32, + AddressType::T_IPv6 => 128, + ); + + return new Subnet($this->address, $this->address, $networkPrefixes[$this->address->getAddressType()]); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::asPattern() + */ + public function asPattern() + { + return new Pattern($this->address, $this->address, 0); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::getSubnetMask() + */ + public function getSubnetMask() + { + if ($this->getAddressType() !== AddressType::T_IPv4) { + return null; + } + + return IPv4::fromBytes(array(255, 255, 255, 255)); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::getReverseDNSLookupName() + */ + public function getReverseDNSLookupName() + { + return array($this->getStartAddress()->getReverseDNSLookupName()); + } +} diff --git a/vendor/mlocati/ip-lib/src/Range/Subnet.php b/vendor/mlocati/ip-lib/src/Range/Subnet.php new file mode 100644 index 0000000..2ce166b --- /dev/null +++ b/vendor/mlocati/ip-lib/src/Range/Subnet.php @@ -0,0 +1,305 @@ +fromAddress = $fromAddress; + $this->toAddress = $toAddress; + $this->networkPrefix = $networkPrefix; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::__toString() + */ + public function __toString() + { + return $this->toString(); + } + + /** + * Try get the range instance starting from its string representation. + * + * @param string|mixed $range + * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses + * + * @return static|null + */ + public static function fromString($range, $supportNonDecimalIPv4 = false) + { + if (!is_string($range)) { + return null; + } + $parts = explode('/', $range); + if (count($parts) !== 2) { + return null; + } + $address = Factory::addressFromString($parts[0], true, true, $supportNonDecimalIPv4); + if ($address === null) { + return null; + } + if (!preg_match('/^[0-9]{1,9}$/', $parts[1])) { + return null; + } + $networkPrefix = (int) $parts[1]; + $addressBytes = $address->getBytes(); + $totalBytes = count($addressBytes); + $numDifferentBits = $totalBytes * 8 - $networkPrefix; + if ($numDifferentBits < 0) { + return null; + } + $numSameBytes = $networkPrefix >> 3; + $sameBytes = array_slice($addressBytes, 0, $numSameBytes); + $differentBytesStart = ($totalBytes === $numSameBytes) ? array() : array_fill(0, $totalBytes - $numSameBytes, 0); + $differentBytesEnd = ($totalBytes === $numSameBytes) ? array() : array_fill(0, $totalBytes - $numSameBytes, 255); + $startSameBits = $networkPrefix % 8; + if ($startSameBits !== 0) { + $varyingByte = $addressBytes[$numSameBytes]; + $differentBytesStart[0] = $varyingByte & bindec(str_pad(str_repeat('1', $startSameBits), 8, '0', STR_PAD_RIGHT)); + $differentBytesEnd[0] = $differentBytesStart[0] + bindec(str_repeat('1', 8 - $startSameBits)); + } + + return new static( + Factory::addressFromBytes(array_merge($sameBytes, $differentBytesStart)), + Factory::addressFromBytes(array_merge($sameBytes, $differentBytesEnd)), + $networkPrefix + ); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::toString() + */ + public function toString($long = false) + { + return $this->fromAddress->toString($long) . '/' . $this->networkPrefix; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::getAddressType() + */ + public function getAddressType() + { + return $this->fromAddress->getAddressType(); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::getStartAddress() + */ + public function getStartAddress() + { + return $this->fromAddress; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::getEndAddress() + */ + public function getEndAddress() + { + return $this->toAddress; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::getComparableStartString() + */ + public function getComparableStartString() + { + return $this->fromAddress->getComparableString(); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::getComparableEndString() + */ + public function getComparableEndString() + { + return $this->toAddress->getComparableString(); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::asSubnet() + */ + public function asSubnet() + { + return $this; + } + + /** + * Get the pattern (asterisk) representation (if applicable) of this range. + * + * @return \IPLib\Range\Pattern|null return NULL if this range can't be represented by a pattern notation + */ + public function asPattern() + { + $address = $this->getStartAddress(); + $networkPrefix = $this->getNetworkPrefix(); + switch ($address->getAddressType()) { + case AddressType::T_IPv4: + return $networkPrefix % 8 === 0 ? new Pattern($address, $address, 4 - $networkPrefix / 8) : null; + case AddressType::T_IPv6: + return $networkPrefix % 16 === 0 ? new Pattern($address, $address, 8 - $networkPrefix / 16) : null; + } + } + + /** + * Get the 6to4 address IPv6 address range. + * + * @return self + */ + public static function get6to4() + { + if (self::$sixToFour === null) { + self::$sixToFour = self::fromString('2002::/16'); + } + + return self::$sixToFour; + } + + /** + * Get subnet prefix. + * + * @return int + */ + public function getNetworkPrefix() + { + return $this->networkPrefix; + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::getSubnetMask() + */ + public function getSubnetMask() + { + if ($this->getAddressType() !== AddressType::T_IPv4) { + return null; + } + $bytes = array(); + $prefix = $this->getNetworkPrefix(); + while ($prefix >= 8) { + $bytes[] = 255; + $prefix -= 8; + } + if ($prefix !== 0) { + $bytes[] = bindec(str_pad(str_repeat('1', $prefix), 8, '0')); + } + $bytes = array_pad($bytes, 4, 0); + + return IPv4::fromBytes($bytes); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::getReverseDNSLookupName() + */ + public function getReverseDNSLookupName() + { + switch ($this->getAddressType()) { + case AddressType::T_IPv4: + $unitSize = 8; // bytes + $maxUnits = 4; + $isHex = false; + $rxUnit = '\d+'; + break; + case AddressType::T_IPv6: + $unitSize = 4; // nibbles + $maxUnits = 32; + $isHex = true; + $rxUnit = '[0-9A-Fa-f]'; + break; + } + $totBits = $unitSize * $maxUnits; + $prefixUnits = (int) ($this->networkPrefix / $unitSize); + $extraBits = ($totBits - $this->networkPrefix) % $unitSize; + if ($extraBits !== 0) { + $prefixUnits += 1; + } + $numVariants = 1 << $extraBits; + $result = array(); + $unitsToRemove = $maxUnits - $prefixUnits; + $initialPointer = preg_replace("/^(({$rxUnit})\.){{$unitsToRemove}}/", '', $this->getStartAddress()->getReverseDNSLookupName()); + $chunks = explode('.', $initialPointer, 2); + for ($index = 0; $index < $numVariants; $index++) { + if ($index !== 0) { + $chunks[0] = $isHex ? dechex(1 + hexdec($chunks[0])) : (string) (1 + (int) $chunks[0]); + } + $result[] = implode('.', $chunks); + } + + return $result; + } +} diff --git a/vendor/mlocati/ip-lib/src/Range/Type.php b/vendor/mlocati/ip-lib/src/Range/Type.php new file mode 100644 index 0000000..cfc268b --- /dev/null +++ b/vendor/mlocati/ip-lib/src/Range/Type.php @@ -0,0 +1,150 @@ +toSameLength($a, $b); + + return $a < $b ? -1 : ($a > $b ? 1 : 0); + } + + /** + * Add 1 to a non-negative integer represented in binary form. + * + * @param string $value + * + * @return string + */ + public function increment($value) + { + $lastZeroIndex = strrpos($value, '0'); + if ($lastZeroIndex === false) { + return '1' . str_repeat('0', strlen($value)); + } + + return ltrim(substr($value, 0, $lastZeroIndex), '0') . '1' . str_repeat('0', strlen($value) - $lastZeroIndex - 1); + } + + /** + * Calculate the bitwise AND of two non-negative integers represented in binary form. + * + * @param string $operand1 + * @param string $operand2 + * + * @return string + */ + public function andX($operand1, $operand2) + { + $operand1 = $this->reduce($operand1); + $operand2 = $this->reduce($operand2); + $numBits = min(strlen($operand1), strlen($operand2)); + $operand1 = substr(str_pad($operand1, $numBits, '0', STR_PAD_LEFT), -$numBits); + $operand2 = substr(str_pad($operand2, $numBits, '0', STR_PAD_LEFT), -$numBits); + $result = ''; + for ($index = 0; $index < $numBits; $index++) { + $result .= $operand1[$index] === '1' && $operand2[$index] === '1' ? '1' : '0'; + } + + return $this->reduce($result); + } + + /** + * Calculate the bitwise OR of two non-negative integers represented in binary form. + * + * @param string $operand1 + * @param string $operand2 + * + * @return string + */ + public function orX($operand1, $operand2) + { + list($operand1, $operand2, $numBits) = $this->toSameLength($operand1, $operand2); + $result = ''; + for ($index = 0; $index < $numBits; $index++) { + $result .= $operand1[$index] === '1' || $operand2[$index] === '1' ? '1' : '0'; + } + + return $result; + } + + /** + * Zero-padding of two non-negative integers represented in binary form, so that they have the same length. + * + * @param string $num1 + * @param string $num2 + * + * @return string[],int[] The first array element is $num1 (padded), the first array element is $num2 (padded), the third array element is the number of bits + */ + private function toSameLength($num1, $num2) + { + $num1 = $this->reduce($num1); + $num2 = $this->reduce($num2); + $numBits = max(strlen($num1), strlen($num2)); + + return array( + str_pad($num1, $numBits, '0', STR_PAD_LEFT), + str_pad($num2, $numBits, '0', STR_PAD_LEFT), + $numBits, + ); + } +} diff --git a/vendor/mlocati/ip-lib/src/Service/RangesFromBounradyCalculator.php b/vendor/mlocati/ip-lib/src/Service/RangesFromBounradyCalculator.php new file mode 100644 index 0000000..c1d29d6 --- /dev/null +++ b/vendor/mlocati/ip-lib/src/Service/RangesFromBounradyCalculator.php @@ -0,0 +1,161 @@ +math = new BinaryMath(); + $this->setNumBits($numBits); + } + + /** + * Calculate the subnets describing all (and only all) the addresses between two bouundaries. + * + * @param \IPLib\Address\AddressInterface $from + * @param \IPLib\Address\AddressInterface $to + * + * @return \IPLib\Range\Subnet[]|null return NULL if the two addresses have an invalid number of bits (that is, different from the one passed to the constructor of this class) + */ + public function getRanges(AddressInterface $from, AddressInterface $to) + { + if ($from->getNumberOfBits() !== $this->numBits || $to->getNumberOfBits() !== $this->numBits) { + return null; + } + if ($from->getComparableString() > $to->getComparableString()) { + list($from, $to) = array($to, $from); + } + $result = array(); + $this->calculate($this->math->reduce($from->getBits()), $this->math->reduce($to->getBits()), $this->numBits, $result); + + return $result; + } + + /** + * Set the number of bits used to represent addresses (32 for IPv4, 128 for IPv6). + * + * @param int $numBits + */ + private function setNumBits($numBits) + { + $numBits = (int) $numBits; + $masks = array(); + $unmasks = array(); + for ($bit = 0; $bit < $numBits; $bit++) { + $masks[$bit] = str_repeat('1', $numBits - $bit) . str_repeat('0', $bit); + $unmasks[$bit] = $bit === 0 ? '0' : str_repeat('1', $bit); + } + $this->numBits = $numBits; + $this->masks = $masks; + $this->unmasks = $unmasks; + } + + /** + * Calculate the subnets. + * + * @param string $start the start address (represented in reduced bit form) + * @param string $end the end address (represented in reduced bit form) + * @param int $position the number of bits in the mask we are comparing at this cycle + * @param \IPLib\Range\Subnet[] $result found ranges will be added to this variable + */ + private function calculate($start, $end, $position, array &$result) + { + if ($start === $end) { + $result[] = $this->subnetFromBits($start, $this->numBits); + + return; + } + for ($index = $position - 1; $index >= 0; $index--) { + $startMasked = $this->math->andX($start, $this->masks[$index]); + $endMasked = $this->math->andX($end, $this->masks[$index]); + if ($startMasked !== $endMasked) { + $position = $index; + break; + } + } + if ($startMasked === $start && $this->math->andX($this->math->increment($end), $this->unmasks[$position]) === '0') { + $result[] = $this->subnetFromBits($start, $this->numBits - 1 - $position); + + return; + } + $middleAddress = $this->math->orX($start, $this->unmasks[$position]); + $this->calculate($start, $middleAddress, $position, $result); + $this->calculate($this->math->increment($middleAddress), $end, $position, $result); + } + + /** + * Create an address instance starting from its bits. + * + * @param string $bits the bits of the address (represented in reduced bit form) + * + * @return \IPLib\Address\AddressInterface + */ + private function addressFromBits($bits) + { + $bits = str_pad($bits, $this->numBits, '0', STR_PAD_LEFT); + $bytes = array(); + foreach (explode("\n", trim(chunk_split($bits, 8, "\n"))) as $byteBits) { + $bytes[] = bindec($byteBits); + } + + return Factory::addressFromBytes($bytes); + } + + /** + * Create an range instance starting from the bits if the address and the length of the network prefix. + * + * @param string $bits the bits of the address (represented in reduced bit form) + * @param int $networkPrefix the length of the network prefix + * + * @return \IPLib\Range\Subnet + */ + private function subnetFromBits($bits, $networkPrefix) + { + $address = $this->addressFromBits($bits); + + return new Subnet($address, $address, $networkPrefix); + } +} From 3dd01b1f7035f9c774b6a2a3c5b2a3720a04975a Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 22 May 2021 10:59:47 +0200 Subject: [PATCH 12/14] testing IP exemption, handle corner cases found in testing --- composer.json | 2 +- composer.lock | 12 ++++++------ lib/Persistence/TrafficLimiter.php | 13 ++++++++----- tst/Persistence/TrafficLimiterTest.php | 15 +++++++++++++++ vendor/paragonie/random_compat/lib/random.php | 19 ++++++++++--------- .../random_compat/phpunit-autoload.php | 14 -------------- 6 files changed, 40 insertions(+), 35 deletions(-) delete mode 100644 vendor/paragonie/random_compat/phpunit-autoload.php diff --git a/composer.json b/composer.json index 98aaedd..a8e98aa 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ }, "require" : { "php" : "^5.6.0 || ^7.0 || ^8.0", - "paragonie/random_compat" : "2.0.19", + "paragonie/random_compat" : "2.0.20", "yzalis/identicon" : "2.0.0", "mlocati/ip-lib" : "1.14.0" }, diff --git a/composer.lock b/composer.lock index 20b0732..931c92f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c3aa7487ba976536cd3db9a60473ce67", + "content-hash": "217f0ba9bdac1014a332a8ba390be949", "packages": [ { "name": "mlocati/ip-lib", @@ -76,16 +76,16 @@ }, { "name": "paragonie/random_compat", - "version": "v2.0.19", + "version": "v2.0.20", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", - "reference": "446fc9faa5c2a9ddf65eb7121c0af7e857295241" + "reference": "0f1f60250fccffeaf5dda91eea1c018aed1adc2a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/446fc9faa5c2a9ddf65eb7121c0af7e857295241", - "reference": "446fc9faa5c2a9ddf65eb7121c0af7e857295241", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/0f1f60250fccffeaf5dda91eea1c018aed1adc2a", + "reference": "0f1f60250fccffeaf5dda91eea1c018aed1adc2a", "shasum": "" }, "require": { @@ -121,7 +121,7 @@ "pseudorandom", "random" ], - "time": "2020-10-15T10:06:57+00:00" + "time": "2021-04-17T09:33:01+00:00" }, { "name": "yzalis/identicon", diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php index af6e497..612a1c9 100644 --- a/lib/Persistence/TrafficLimiter.php +++ b/lib/Persistence/TrafficLimiter.php @@ -121,19 +121,22 @@ class TrafficLimiter extends AbstractPersistence $address = \IPLib\Factory::addressFromString($_SERVER[self::$_ipKey]); $range = \IPLib\Factory::rangeFromString(trim($ipRange)); - // If $range is null something went wrong (possible invalid ip given in config). It's here becaue matches($range) does not accepts null vallue + // address could not be parsed, we might not be in IP space and try a string comparison instead + if ($address == null) { + return $_SERVER[self::$_ipKey] === $ipRange; + } + // range could not be parsed, possibly an invalid ip range given in config if ($range == null) { return false; } - // Ip-lib does throws and exception when something goes wrong, if so we want to catch it and set contained to false + // Ip-lib throws an exception when something goes wrong, if so we want to catch it and set contained to false try { return $address->matches($range); } catch (\Exception $e) { - // If something is wrong with matching the ip, we do nothing + // If something is wrong with matching the ip, we assume it doesn't match + return false; } - - return false; } /** diff --git a/tst/Persistence/TrafficLimiterTest.php b/tst/Persistence/TrafficLimiterTest.php index 7376938..4101301 100644 --- a/tst/Persistence/TrafficLimiterTest.php +++ b/tst/Persistence/TrafficLimiterTest.php @@ -35,5 +35,20 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase $this->assertTrue(TrafficLimiter::canPass(), 'fourth request has different ip and may pass'); $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; $this->assertFalse(TrafficLimiter::canPass(), 'fifth request is to fast, may not pass'); + + // exempted IPs configuration + TrafficLimiter::setExemptedIp('1.2.3.4,10.10.10.0/24,2001:1620:2057::/48'); + $this->assertFalse(TrafficLimiter::canPass(), 'still too fast and not exempted'); + $_SERVER['REMOTE_ADDR'] = '10.10.10.10'; + $this->assertTrue(TrafficLimiter::canPass(), 'IPv4 in exempted range'); + $this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but IPv4 in exempted range'); + $_SERVER['REMOTE_ADDR'] = '2001:1620:2057:dead:beef::cafe:babe'; + $this->assertTrue(TrafficLimiter::canPass(), 'IPv6 in exempted range'); + $this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but IPv6 in exempted range'); + TrafficLimiter::setExemptedIp('127.*,foobar'); + $this->assertFalse(TrafficLimiter::canPass(), 'request is to fast, invalid range'); + $_SERVER['REMOTE_ADDR'] = 'foobar'; + $this->assertTrue(TrafficLimiter::canPass(), 'non-IP address'); + $this->assertTrue(TrafficLimiter::canPass(), 'request is to fast, but non-IP address matches exempted range'); } } diff --git a/vendor/paragonie/random_compat/lib/random.php b/vendor/paragonie/random_compat/lib/random.php index 36245f5..6df1cb0 100644 --- a/vendor/paragonie/random_compat/lib/random.php +++ b/vendor/paragonie/random_compat/lib/random.php @@ -54,9 +54,9 @@ if (!defined('RANDOM_COMPAT_READ_BUFFER')) { $RandomCompatDIR = dirname(__FILE__); -require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'byte_safe_strings.php'; -require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'cast_to_int.php'; -require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'error_polyfill.php'; +require_once $RandomCompatDIR.DIRECTORY_SEPARATOR.'byte_safe_strings.php'; +require_once $RandomCompatDIR.DIRECTORY_SEPARATOR.'cast_to_int.php'; +require_once $RandomCompatDIR.DIRECTORY_SEPARATOR.'error_polyfill.php'; if (!is_callable('random_bytes')) { /** @@ -76,9 +76,9 @@ if (!is_callable('random_bytes')) { if (extension_loaded('libsodium')) { // See random_bytes_libsodium.php if (PHP_VERSION_ID >= 50300 && is_callable('\\Sodium\\randombytes_buf')) { - require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'random_bytes_libsodium.php'; + require_once $RandomCompatDIR.DIRECTORY_SEPARATOR.'random_bytes_libsodium.php'; } elseif (method_exists('Sodium', 'randombytes_buf')) { - require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'random_bytes_libsodium_legacy.php'; + require_once $RandomCompatDIR.DIRECTORY_SEPARATOR.'random_bytes_libsodium_legacy.php'; } } @@ -117,7 +117,7 @@ if (!is_callable('random_bytes')) { // place, that is not helpful to us here. // See random_bytes_dev_urandom.php - require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'random_bytes_dev_urandom.php'; + require_once $RandomCompatDIR.DIRECTORY_SEPARATOR.'random_bytes_dev_urandom.php'; } // Unset variables after use $RandomCompat_basedir = null; @@ -159,7 +159,7 @@ if (!is_callable('random_bytes')) { extension_loaded('mcrypt') ) { // See random_bytes_mcrypt.php - require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'random_bytes_mcrypt.php'; + require_once $RandomCompatDIR.DIRECTORY_SEPARATOR.'random_bytes_mcrypt.php'; } $RandomCompatUrandom = null; @@ -182,9 +182,10 @@ if (!is_callable('random_bytes')) { if (!in_array('com', $RandomCompat_disabled_classes)) { try { $RandomCompatCOMtest = new COM('CAPICOM.Utilities.1'); + /** @psalm-suppress TypeDoesNotContainType */ if (method_exists($RandomCompatCOMtest, 'GetRandom')) { // See random_bytes_com_dotnet.php - require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'random_bytes_com_dotnet.php'; + require_once $RandomCompatDIR.DIRECTORY_SEPARATOR.'random_bytes_com_dotnet.php'; } } catch (com_exception $e) { // Don't try to use it. @@ -219,7 +220,7 @@ if (!is_callable('random_bytes')) { } if (!is_callable('random_int')) { - require_once $RandomCompatDIR . DIRECTORY_SEPARATOR . 'random_int.php'; + require_once $RandomCompatDIR.DIRECTORY_SEPARATOR.'random_int.php'; } $RandomCompatDIR = null; diff --git a/vendor/paragonie/random_compat/phpunit-autoload.php b/vendor/paragonie/random_compat/phpunit-autoload.php deleted file mode 100644 index 87b01aa..0000000 --- a/vendor/paragonie/random_compat/phpunit-autoload.php +++ /dev/null @@ -1,14 +0,0 @@ -= 5.3 - * - * Class PHPUnit_Framework_TestCase - */ -if (PHP_VERSION_ID >= 50300) { - if (!class_exists('PHPUnit_Framework_TestCase')) { - require_once __DIR__ . '/other/phpunit-shim.php'; - } -} From 84771d716713a51aadc84ab2721c01f7c21fc97e Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 22 May 2021 11:01:16 +0200 Subject: [PATCH 13/14] documenting changes --- CHANGELOG.md | 2 ++ CREDITS.md | 1 + 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e416b9..0a8ed40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,9 @@ * ADDED: new HTTP headers improving security (#765) * ADDED: Download button for paste text (#774) * ADDED: Opt-out of federated learning of cohorts (FLoC) (#776) + * ADDED: Configuration option to exempt ips from the rate-limiter (#787) * CHANGED: Language selection cookie only transmitted over HTTPS (#472) + * CHANGED: Upgrading libraries to: random_compat 2.0.20 * **1.3.5 (2021-04-05)** * ADDED: Translation for Hebrew, Lithuanian, Indonesian and Catalan * ADDED: Make the project info configurable (#681) diff --git a/CREDITS.md b/CREDITS.md index 1aabf6f..338c2df 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -27,6 +27,7 @@ Sébastien Sauvage - original idea and main developer * Harald Leithner - base58 encoding of key * Haocen - lots of bugfixes and UI improvements * Lucas Savva - configurable config file location, NixOS packaging +* rodehoed - option to exempt ips from the rate-limiter ## Translations * Hexalyse - French From 91c8f9f23c9a302c567b328eef73eb49540f2872 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 22 May 2021 11:02:54 +0200 Subject: [PATCH 14/14] use namespaces --- lib/Persistence/TrafficLimiter.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php index 612a1c9..dedf588 100644 --- a/lib/Persistence/TrafficLimiter.php +++ b/lib/Persistence/TrafficLimiter.php @@ -13,6 +13,8 @@ namespace PrivateBin\Persistence; +use Exception; +use IPLib\Factory; use PrivateBin\Configuration; /** @@ -118,8 +120,8 @@ class TrafficLimiter extends AbstractPersistence private static function matchIp($ipRange = null) { // Match $_ipKey to $ipRange and if it matches it will return with a true - $address = \IPLib\Factory::addressFromString($_SERVER[self::$_ipKey]); - $range = \IPLib\Factory::rangeFromString(trim($ipRange)); + $address = Factory::addressFromString($_SERVER[self::$_ipKey]); + $range = Factory::rangeFromString(trim($ipRange)); // address could not be parsed, we might not be in IP space and try a string comparison instead if ($address == null) { @@ -133,7 +135,7 @@ class TrafficLimiter extends AbstractPersistence // Ip-lib throws an exception when something goes wrong, if so we want to catch it and set contained to false try { return $address->matches($range); - } catch (\Exception $e) { + } catch (Exception $e) { // If something is wrong with matching the ip, we assume it doesn't match return false; }