diff --git a/CHANGELOG.md b/CHANGELOG.md index 033d2453..b151d11d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ * ADDED: Added `shortenviashlink` endpoint with an `shlink` configuration section * ADDED: Password peek (#1254) * CHANGED: CSP recommendation around bootstrap5 template resolved in Firefox 131 (#1613) -* CHANGED: Upgrading libraries to: bootstrap 5.3.8 & DOMpurify 3.2.7 +* CHANGED: Upgrading libraries to: bootstrap 5.3.8, DOMpurify 3.2.7 & ip-lib 1.21.0 * FIXED: Allow pasting a password for decrypting a paste (#1620) * FIXED: Allow copying the shortened link after using a URL shortener (#1624) * FIXED: URL extraction fails when frame-ancestors is set in CSP (#1644) diff --git a/composer.json b/composer.json index 3338e1ab..134eef14 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,7 @@ "require" : { "php": "^7.4 || ^8.0", "jdenticon/jdenticon": "2.0.0", - "mlocati/ip-lib": "1.20.0", + "mlocati/ip-lib": "1.21.0", "symfony/polyfill-php80": "1.31.0", "yzalis/identicon": "2.0.0" }, diff --git a/composer.lock b/composer.lock index b032e9c4..dac377f9 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": "cc778a671eac2ba1ec70bf9398b2e1bf", + "content-hash": "f008f704483472b237031d4fa389cb70", "packages": [ { "name": "jdenticon/jdenticon", @@ -57,16 +57,16 @@ }, { "name": "mlocati/ip-lib", - "version": "1.20.0", + "version": "1.21.0", "source": { "type": "git", "url": "https://github.com/mlocati/ip-lib.git", - "reference": "fd45fc3bf08ed6c7e665e2e70562082ac954afd4" + "reference": "b5d38cdcbfc1516604d821a1f3f4a1638f327267" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mlocati/ip-lib/zipball/fd45fc3bf08ed6c7e665e2e70562082ac954afd4", - "reference": "fd45fc3bf08ed6c7e665e2e70562082ac954afd4", + "url": "https://api.github.com/repos/mlocati/ip-lib/zipball/b5d38cdcbfc1516604d821a1f3f4a1638f327267", + "reference": "b5d38cdcbfc1516604d821a1f3f4a1638f327267", "shasum": "" }, "require": { @@ -112,7 +112,7 @@ ], "support": { "issues": "https://github.com/mlocati/ip-lib/issues", - "source": "https://github.com/mlocati/ip-lib/tree/1.20.0" + "source": "https://github.com/mlocati/ip-lib/tree/1.21.0" }, "funding": [ { @@ -124,7 +124,7 @@ "type": "other" } ], - "time": "2025-02-04T17:30:58+00:00" + "time": "2025-09-24T13:58:50+00:00" }, { "name": "symfony/polyfill-php80", @@ -337,16 +337,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.13.3", + "version": "1.13.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "faed855a7b5f4d4637717c2b3863e277116beb36" + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/faed855a7b5f4d4637717c2b3863e277116beb36", - "reference": "faed855a7b5f4d4637717c2b3863e277116beb36", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { @@ -385,7 +385,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.3" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { @@ -393,20 +393,20 @@ "type": "tidelift" } ], - "time": "2025-07-05T12:25:42+00:00" + "time": "2025-08-01T08:46:24+00:00" }, { "name": "nikic/php-parser", - "version": "v5.5.0", + "version": "v5.6.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "ae59794362fe85e051a58ad36b289443f57be7a9" + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9", - "reference": "ae59794362fe85e051a58ad36b289443f57be7a9", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", "shasum": "" }, "require": { @@ -425,7 +425,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -449,9 +449,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1" }, - "time": "2025-05-31T08:24:38+00:00" + "time": "2025-08-13T20:13:15+00:00" }, { "name": "phar-io/manifest", @@ -892,16 +892,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.23", + "version": "9.6.29", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95" + "reference": "9ecfec57835a5581bc888ea7e13b51eb55ab9dd3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", - "reference": "43d2cb18d0675c38bd44982a5d1d88f6d53d8d95", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9ecfec57835a5581bc888ea7e13b51eb55ab9dd3", + "reference": "9ecfec57835a5581bc888ea7e13b51eb55ab9dd3", "shasum": "" }, "require": { @@ -912,7 +912,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.13.1", + "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=7.3", @@ -923,11 +923,11 @@ "phpunit/php-timer": "^5.0.3", "sebastian/cli-parser": "^1.0.2", "sebastian/code-unit": "^1.0.8", - "sebastian/comparator": "^4.0.8", + "sebastian/comparator": "^4.0.9", "sebastian/diff": "^4.0.6", "sebastian/environment": "^5.1.5", - "sebastian/exporter": "^4.0.6", - "sebastian/global-state": "^5.0.7", + "sebastian/exporter": "^4.0.8", + "sebastian/global-state": "^5.0.8", "sebastian/object-enumerator": "^4.0.4", "sebastian/resource-operations": "^3.0.4", "sebastian/type": "^3.2.1", @@ -975,7 +975,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.23" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.29" }, "funding": [ { @@ -999,7 +999,7 @@ "type": "tidelift" } ], - "time": "2025-05-02T06:40:34+00:00" + "time": "2025-09-24T06:29:11+00:00" }, { "name": "sebastian/cli-parser", @@ -1170,16 +1170,16 @@ }, { "name": "sebastian/comparator", - "version": "4.0.8", + "version": "4.0.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + "reference": "67a2df3a62639eab2cc5906065e9805d4fd5dfc5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/67a2df3a62639eab2cc5906065e9805d4fd5dfc5", + "reference": "67a2df3a62639eab2cc5906065e9805d4fd5dfc5", "shasum": "" }, "require": { @@ -1232,15 +1232,27 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.9" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" } ], - "time": "2022-09-14T12:41:17+00:00" + "time": "2025-08-10T06:51:50+00:00" }, { "name": "sebastian/complexity", @@ -1430,16 +1442,16 @@ }, { "name": "sebastian/exporter", - "version": "4.0.6", + "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", - "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/14c6ba52f95a36c3d27c835d65efc7123c446e8c", + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c", "shasum": "" }, "require": { @@ -1495,28 +1507,40 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" } ], - "time": "2024-03-02T06:33:00+00:00" + "time": "2025-09-24T06:03:27+00:00" }, { "name": "sebastian/global-state", - "version": "5.0.7", + "version": "5.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", - "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b6781316bdcd28260904e7cc18ec983d0d2ef4f6", + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6", "shasum": "" }, "require": { @@ -1559,15 +1583,27 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", + "type": "tidelift" } ], - "time": "2024-03-02T06:35:11+00:00" + "time": "2025-08-10T07:10:35+00:00" }, { "name": "sebastian/lines-of-code", @@ -1740,16 +1776,16 @@ }, { "name": "sebastian/recursion-context", - "version": "4.0.5", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/539c6691e0623af6dc6f9c20384c120f963465a0", + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0", "shasum": "" }, "require": { @@ -1791,15 +1827,27 @@ "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.6" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2023-02-03T06:07:39+00:00" + "time": "2025-08-10T06:57:39+00:00" }, { "name": "sebastian/resource-operations", diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 113f8533..28f4f04c 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -22,6 +22,7 @@ return array( '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\\NumberInChunks' => $vendorDir . '/mlocati/ip-lib/src/Service/NumberInChunks.php', 'IPLib\\Service\\RangesFromBoundaryCalculator' => $vendorDir . '/mlocati/ip-lib/src/Service/RangesFromBoundaryCalculator.php', 'IPLib\\Service\\UnsignedIntegerMath' => $vendorDir . '/mlocati/ip-lib/src/Service/UnsignedIntegerMath.php', 'Identicon\\Generator\\BaseGenerator' => $vendorDir . '/yzalis/identicon/src/Identicon/Generator/BaseGenerator.php', diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index ef0e05a6..e9194d01 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -70,6 +70,7 @@ class ComposerStaticInitDontChange '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\\NumberInChunks' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Service/NumberInChunks.php', 'IPLib\\Service\\RangesFromBoundaryCalculator' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Service/RangesFromBoundaryCalculator.php', 'IPLib\\Service\\UnsignedIntegerMath' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Service/UnsignedIntegerMath.php', 'Identicon\\Generator\\BaseGenerator' => __DIR__ . '/..' . '/yzalis/identicon/src/Identicon/Generator/BaseGenerator.php', diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 8519f72e..56dccd55 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -3,7 +3,7 @@ 'name' => 'privatebin/privatebin', 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => '3ba29ea29e04d8a16d64e0f49994ba416c1b008f', + 'reference' => '06496a1b0e975b79c5a7abc0bd54b492ca264640', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -20,9 +20,9 @@ 'dev_requirement' => false, ), 'mlocati/ip-lib' => array( - 'pretty_version' => '1.20.0', - 'version' => '1.20.0.0', - 'reference' => 'fd45fc3bf08ed6c7e665e2e70562082ac954afd4', + 'pretty_version' => '1.21.0', + 'version' => '1.21.0.0', + 'reference' => 'b5d38cdcbfc1516604d821a1f3f4a1638f327267', 'type' => 'library', 'install_path' => __DIR__ . '/../mlocati/ip-lib', 'aliases' => array(), @@ -31,7 +31,7 @@ 'privatebin/privatebin' => array( 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => '3ba29ea29e04d8a16d64e0f49994ba416c1b008f', + 'reference' => '06496a1b0e975b79c5a7abc0bd54b492ca264640', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), diff --git a/vendor/mlocati/ip-lib/src/Address/AddressInterface.php b/vendor/mlocati/ip-lib/src/Address/AddressInterface.php index c367a4d5..0dcae827 100644 --- a/vendor/mlocati/ip-lib/src/Address/AddressInterface.php +++ b/vendor/mlocati/ip-lib/src/Address/AddressInterface.php @@ -110,11 +110,12 @@ interface AddressInterface /** * Get the address at a certain distance from this address. * - * @param int $n the distance of the address (can be negative) + * @param int|numeric-string $n the distance of the address (can be negative) * - * @return \IPLib\Address\AddressInterface|null return NULL if $n is not an integer or if the final address would be invalid + * @return \IPLib\Address\AddressInterface|null return NULL if $n is not an integer or NULL if $n is neither an integer nor a string containing a valid integer, or if the final address would be invalid * * @since 1.15.0 + * @since 1.21.0 $n can also be a numeric string * * @example passing 1 to the address 127.0.0.1 will result in 127.0.0.2 * @example passing -1 to the address 127.0.0.1 will result in 127.0.0.0 diff --git a/vendor/mlocati/ip-lib/src/Address/IPv4.php b/vendor/mlocati/ip-lib/src/Address/IPv4.php index 1fa109de..c59f0025 100644 --- a/vendor/mlocati/ip-lib/src/Address/IPv4.php +++ b/vendor/mlocati/ip-lib/src/Address/IPv4.php @@ -6,6 +6,8 @@ use IPLib\ParseStringFlag; use IPLib\Range\RangeInterface; use IPLib\Range\Subnet; use IPLib\Range\Type as RangeType; +use IPLib\Service\BinaryMath; +use IPLib\Service\NumberInChunks; /** * An IPv4 address. @@ -456,28 +458,24 @@ class IPv4 implements AddressInterface */ public function getAddressAtOffset($n) { - if (!is_int($n)) { + if (is_int($n)) { + $thatChunks = NumberInChunks::fromInteger($n, NumberInChunks::CHUNKSIZE_BYTES); + } elseif (($s = BinaryMath::getInstance()->normalizeIntegerString($n)) !== '') { + $thatChunks = NumberInChunks::fromNumericString($s, NumberInChunks::CHUNKSIZE_BYTES); + } else { + return null; + } + $myBytes = $this->getBytes(); + while (isset($myBytes[1]) && $myBytes[0] === 0) { + array_shift($myBytes); + } + $myChunks = new NumberInChunks(false, $myBytes, NumberInChunks::CHUNKSIZE_BYTES); + $result = $myChunks->add($thatChunks); + if ($result->negative || count($result->chunks) > 4) { return null; } - $boundary = 256; - $mod = $n; - $bytes = $this->getBytes(); - for ($i = count($bytes) - 1; $i >= 0; $i--) { - $tmp = ($bytes[$i] + $mod) % $boundary; - $mod = (int) floor(($bytes[$i] + $mod) / $boundary); - if ($tmp < 0) { - $tmp += $boundary; - } - - $bytes[$i] = $tmp; - } - - if ($mod !== 0) { - return null; - } - - return static::fromBytes($bytes); + return static::fromBytes(array_pad($result->chunks, -4, 0)); } /** diff --git a/vendor/mlocati/ip-lib/src/Address/IPv6.php b/vendor/mlocati/ip-lib/src/Address/IPv6.php index ad7e2a7f..80749e33 100644 --- a/vendor/mlocati/ip-lib/src/Address/IPv6.php +++ b/vendor/mlocati/ip-lib/src/Address/IPv6.php @@ -6,6 +6,8 @@ use IPLib\ParseStringFlag; use IPLib\Range\RangeInterface; use IPLib\Range\Subnet; use IPLib\Range\Type as RangeType; +use IPLib\Service\BinaryMath; +use IPLib\Service\NumberInChunks; /** * An IPv6 address. @@ -549,28 +551,24 @@ class IPv6 implements AddressInterface */ public function getAddressAtOffset($n) { - if (!is_int($n)) { + if (is_int($n)) { + $thatChunks = NumberInChunks::fromInteger($n, NumberInChunks::CHUNKSIZE_WORDS); + } elseif (($s = BinaryMath::getInstance()->normalizeIntegerString($n)) !== '') { + $thatChunks = NumberInChunks::fromNumericString($s, NumberInChunks::CHUNKSIZE_WORDS); + } else { + return null; + } + $myWords = $this->getWords(); + while (isset($myWords[1]) && $myWords[0] === 0) { + array_shift($myWords); + } + $myChunks = new NumberInChunks(false, $myWords, NumberInChunks::CHUNKSIZE_WORDS); + $result = $myChunks->add($thatChunks); + if ($result->negative || count($result->chunks) > 8) { return null; } - $boundary = 0x10000; - $mod = $n; - $words = $this->getWords(); - for ($i = count($words) - 1; $i >= 0; $i--) { - $tmp = ($words[$i] + $mod) % $boundary; - $mod = (int) floor(($words[$i] + $mod) / $boundary); - if ($tmp < 0) { - $tmp += $boundary; - } - - $words[$i] = $tmp; - } - - if ($mod !== 0) { - return null; - } - - return static::fromWords($words); + return static::fromWords(array_pad($result->chunks, -8, 0)); } /** @@ -645,7 +643,7 @@ class IPv6 implements AddressInterface } $myWords = $this->getWords(); $otherWords = $other->getWords(); - $sum = array_fill(0, 7, 0); + $sum = array_fill(0, 8, 0); $carry = 0; for ($index = 7; $index >= 0; $index--) { $word = $myWords[$index] + $otherWords[$index] + $carry; diff --git a/vendor/mlocati/ip-lib/src/Range/AbstractRange.php b/vendor/mlocati/ip-lib/src/Range/AbstractRange.php index 24783ca5..9faea570 100644 --- a/vendor/mlocati/ip-lib/src/Range/AbstractRange.php +++ b/vendor/mlocati/ip-lib/src/Range/AbstractRange.php @@ -7,6 +7,7 @@ use IPLib\Address\IPv4; use IPLib\Address\IPv6; use IPLib\Address\Type as AddressType; use IPLib\Factory; +use IPLib\Service\BinaryMath; use OutOfBoundsException; /** @@ -59,17 +60,26 @@ abstract class AbstractRange implements RangeInterface */ public function getAddressAtOffset($n) { - if (!is_int($n)) { + if (is_int($n)) { + $positive = $n >= 0; + if ($positive === false) { + $nPlus1 = $n + 1; + } + } elseif (($s = BinaryMath::getInstance()->normalizeIntegerString($n)) !== '') { + $n = $s; + $positive = $n[0] !== '-'; + if ($positive === false) { + $nPlus1 = BinaryMath::getInstance()->add1ToIntegerString($n); + } + } else { return null; } - - $address = null; - if ($n >= 0) { + if ($positive) { $start = Factory::parseAddressString($this->getComparableStartString()); $address = $start->getAddressAtOffset($n); } else { $end = Factory::parseAddressString($this->getComparableEndString()); - $address = $end->getAddressAtOffset($n + 1); + $address = $end->getAddressAtOffset($nPlus1); } if ($address === null) { diff --git a/vendor/mlocati/ip-lib/src/Range/Pattern.php b/vendor/mlocati/ip-lib/src/Range/Pattern.php index f7afeebb..b14e744c 100644 --- a/vendor/mlocati/ip-lib/src/Range/Pattern.php +++ b/vendor/mlocati/ip-lib/src/Range/Pattern.php @@ -7,6 +7,7 @@ use IPLib\Address\IPv4; use IPLib\Address\IPv6; use IPLib\Address\Type as AddressType; use IPLib\ParseStringFlag; +use IPLib\Service\BinaryMath; /** * Represents an address range in pattern format (only ending asterisks are supported). @@ -304,7 +305,21 @@ class Pattern extends AbstractRange $maxPrefix = $fromAddress::getNumberOfBits(); $prefix = $this->getNetworkPrefix(); - return pow(2, ($maxPrefix - $prefix)); + return pow(2, $maxPrefix - $prefix); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::getExactSize() + */ + public function getExactSize() + { + $fromAddress = $this->fromAddress; + $maxPrefix = $fromAddress::getNumberOfBits(); + $prefix = $this->getNetworkPrefix(); + + return BinaryMath::getInstance()->pow2string($maxPrefix - $prefix); } /** diff --git a/vendor/mlocati/ip-lib/src/Range/RangeInterface.php b/vendor/mlocati/ip-lib/src/Range/RangeInterface.php index 4c7c02d6..4b27cd18 100644 --- a/vendor/mlocati/ip-lib/src/Range/RangeInterface.php +++ b/vendor/mlocati/ip-lib/src/Range/RangeInterface.php @@ -46,11 +46,12 @@ interface RangeInterface /** * Get the address at a certain offset of this range. * - * @param int $n the offset of the address (support negative offset) + * @param int|numeric-string $n the offset of the address (support negative offset) * - * @return \IPLib\Address\AddressInterface|null return NULL if $n is not an integer or if the offset out of range + * @return \IPLib\Address\AddressInterface|null return NULL if $n is neither an integer nor a string containing a valid integer, or if the offset out of range * * @since 1.15.0 + * @since 1.21.0 $n can also be a numeric string * * @example passing 256 to the range 127.0.0.0/16 will result in 127.0.1.0 * @example passing -1 to the range 127.0.1.0/16 will result in 127.0.255.255 @@ -150,14 +151,23 @@ interface RangeInterface public function getReverseDNSLookupName(); /** - * Get the count of addresses this IP range contains. + * Get the count of addresses contained in this IP range (possibly approximated). * - * @return int|float Return float as for huge IPv6 networks, int is not enough + * @return int|float If the number of addresses exceeds PHP_INT_MAX a float containing an approximation will be returned * * @since 1.16.0 */ public function getSize(); + /** + * Get the exact count of addresses contained in this IP range. + * + * @return int|numeric-string If the number of addresses exceeds PHP_INT_MAX a string containing the exact number of addresses will be returned + * + * @since 1.21.0 + */ + public function getExactSize(); + /** * Get the "network prefix", that is how many bits of the address are dedicated to the network portion. * diff --git a/vendor/mlocati/ip-lib/src/Range/Single.php b/vendor/mlocati/ip-lib/src/Range/Single.php index 09af4d8d..bcc6960a 100644 --- a/vendor/mlocati/ip-lib/src/Range/Single.php +++ b/vendor/mlocati/ip-lib/src/Range/Single.php @@ -237,6 +237,16 @@ class Single extends AbstractRange return 1; } + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::getExactSize() + */ + public function getExactSize() + { + return 1; + } + /** * {@inheritdoc} * diff --git a/vendor/mlocati/ip-lib/src/Range/Subnet.php b/vendor/mlocati/ip-lib/src/Range/Subnet.php index 9c00a101..4dcbb54c 100644 --- a/vendor/mlocati/ip-lib/src/Range/Subnet.php +++ b/vendor/mlocati/ip-lib/src/Range/Subnet.php @@ -7,6 +7,7 @@ use IPLib\Address\IPv4; use IPLib\Address\Type as AddressType; use IPLib\Factory; use IPLib\ParseStringFlag; +use IPLib\Service\BinaryMath; /** * Represents an address range in subnet format (eg CIDR). @@ -348,6 +349,20 @@ class Subnet extends AbstractRange $maxPrefix = $fromAddress::getNumberOfBits(); $prefix = $this->getNetworkPrefix(); - return pow(2, ($maxPrefix - $prefix)); + return pow(2, $maxPrefix - $prefix); + } + + /** + * {@inheritdoc} + * + * @see \IPLib\Range\RangeInterface::getExactSize() + */ + public function getExactSize() + { + $fromAddress = $this->fromAddress; + $maxPrefix = $fromAddress::getNumberOfBits(); + $prefix = $this->getNetworkPrefix(); + + return BinaryMath::getInstance()->pow2string($maxPrefix - $prefix); } } diff --git a/vendor/mlocati/ip-lib/src/Range/Type.php b/vendor/mlocati/ip-lib/src/Range/Type.php index b2ba8eb7..9c1b290f 100644 --- a/vendor/mlocati/ip-lib/src/Range/Type.php +++ b/vendor/mlocati/ip-lib/src/Range/Type.php @@ -120,29 +120,29 @@ class Type case static::T_UNSPECIFIED: return 'Unspecified/unknown address'; case static::T_RESERVED: - return 'Reserved/internal use only'; + return 'Reserved/internal use only'; case static::T_THISNETWORK: - return 'Refer to source hosts on "this" network'; + return 'Refer to source hosts on "this" network'; case static::T_LOOPBACK: - return 'Internet host loopback address'; + return 'Internet host loopback address'; case static::T_ANYCASTRELAY: - return 'Relay anycast address'; + return 'Relay anycast address'; case static::T_LIMITEDBROADCAST: - return '"Limited broadcast" destination address'; + return '"Limited broadcast" destination address'; case static::T_MULTICAST: - return 'Multicast address assignments - Indentify a group of interfaces'; + return 'Multicast address assignments - Indentify a group of interfaces'; case static::T_LINKLOCAL: - return '"Link local" address, allocated for communication between hosts on a single link'; + return '"Link local" address, allocated for communication between hosts on a single link'; case static::T_LINKLOCAL_UNICAST: return 'Link local unicast / Linked-scoped unicast'; case static::T_DISCARDONLY: - return 'Discard only'; + return 'Discard only'; case static::T_DISCARD: - return 'Discard'; + return 'Discard'; case static::T_PRIVATENETWORK: - return 'For use in private networks'; + return 'For use in private networks'; case static::T_PUBLIC: - return 'Public address'; + return 'Public address'; case static::T_CGNAT: return 'Carrier-grade NAT'; default: diff --git a/vendor/mlocati/ip-lib/src/Service/BinaryMath.php b/vendor/mlocati/ip-lib/src/Service/BinaryMath.php index 727fecf9..30bb94b6 100644 --- a/vendor/mlocati/ip-lib/src/Service/BinaryMath.php +++ b/vendor/mlocati/ip-lib/src/Service/BinaryMath.php @@ -9,6 +9,20 @@ namespace IPLib\Service; */ class BinaryMath { + private static $instance; + + /** + * @return \IPLib\Service\BinaryMath + */ + public static function getInstance() + { + if (self::$instance === null) { + self::$instance = new self(); + } + + return self::$instance; + } + /** * Trim the leading zeroes from a non-negative integer represented in binary form. * @@ -97,6 +111,104 @@ class BinaryMath return $result; } + /** + * Compute 2 raised to the given exponent. + * + * If the result fits into a native PHP integer, an int is returned. + * If the result exceeds PHP_INT_MAX, a string containing the exact decimal representation is returned. + * + * @param int $exponent The non-negative exponent + * + * @return int|string + */ + public function pow2string($exponent) + { + if ($exponent < PHP_INT_SIZE * 8 - 1) { + return 1 << $exponent; + } + $digits = array(1); + for ($i = 0; $i < $exponent; $i++) { + $carry = 0; + foreach ($digits as $index => $digit) { + $product = $digit * 2 + $carry; + $digits[$index] = $product % 10; + $carry = (int) ($product / 10); + } + if ($carry !== 0) { + $digits[] = $carry; + } + } + + return implode('', array_reverse($digits)); + } + + /** + * @param numeric-string|mixed $value + * + * @return string empty string if $value is not a valid numeric string + */ + public function normalizeIntegerString($value) + { + if (!is_string($value) || $value === '') { + return ''; + } + $sign = $value[0]; + if ($sign === '-' || $sign === '+') { + $value = substr($value, 1); + } + $matches = null; + if (!preg_match('/^0*([0-9]+)$/', $value, $matches)) { + return ''; + } + + return ($sign === '-' && $matches[1] !== '0' ? $sign : '') . $matches[1]; + } + + /** + * @param numeric-string $value a string that has been normalized with normalizeIntegerString() + * + * @return string + */ + public function add1ToIntegerString($value) + { + if ($value[0] === '-') { + if ($value === '-1') { + return '0'; + } + $digits = str_split(substr($value, 1)); + $i = count($digits) - 1; + while ($i >= 0) { + if ($digits[$i] !== '0') { + $digits[$i] = (string) ((int) $digits[$i] - 1); + break; + } + $digits[$i] = '9'; + $i--; + } + $imploded = implode('', $digits); + if ($imploded[0] === '0') { + $imploded = substr($imploded, 1); + } + + return '-' . $imploded; + } + $digits = str_split($value); + $carry = 1; + for ($i = count($digits) - 1; $i >= 0; $i--) { + $sum = (int) $digits[$i] + $carry; + $digits[$i] = (string) ($sum % 10); + $carry = (int) ($sum / 10); + if ($carry === 0) { + break; + } + if ($i === 0) { + array_unshift($digits, (string) $carry); + } + } + + return implode('', $digits); + } + /** * Zero-padding of two non-negative integers represented in binary form, so that they have the same length. * diff --git a/vendor/mlocati/ip-lib/src/Service/NumberInChunks.php b/vendor/mlocati/ip-lib/src/Service/NumberInChunks.php new file mode 100644 index 00000000..67fc6e80 --- /dev/null +++ b/vendor/mlocati/ip-lib/src/Service/NumberInChunks.php @@ -0,0 +1,253 @@ +negative = $negative; + $this->chunks = $chunks; + $this->chunkSize = $chunkSize; + } + + /** + * @throws \InvalidArgumentException if $other has a $chunkSize that's not the same as the $chunkSize of this + * + * @return \IPLib\Service\NumberInChunks + */ + public function negate() + { + return new self($this->chunks === array(0) ? false : !$this->negative, $this->chunks, $this->chunkSize); + } + + /** + * @throws \InvalidArgumentException if $other has a $chunkSize that's not the same as the $chunkSize of this + * + * @return \IPLib\Service\NumberInChunks + */ + public function add(NumberInChunks $that) + { + if ($this->chunkSize !== $that->chunkSize) { + throw new InvalidArgumentException('Incompatible chunk size'); + } + if ($this->negative === $that->negative) { + return new self($this->negative, self::addChunks($this->chunks, $that->chunks, $this->chunkSize), $this->chunkSize); + } + if ($that->negative) { + list($negative, $chunks) = self::substractChunks($this->chunks, $that->chunks, $this->chunkSize); + } else { + list($negative, $chunks) = self::substractChunks($that->chunks, $this->chunks, $this->chunkSize); + } + + return new self($negative, $chunks, $this->chunkSize); + } + + /** + * @param int $int + * @param int $chunkSize + * + * @return \IPLib\Service\NumberInChunks + */ + public static function fromInteger($int, $chunkSize) + { + if ($int === 0) { + return new self(false, array(0), $chunkSize); + } + $negative = $int < 0; + if ($negative) { + $positiveInt = -$int; + if (is_float($positiveInt)) { // -PHP_INT_MIN is bigger than PHP_INT_MAX + return self::fromNumericString((string) $int, $chunkSize); + } + $int = $positiveInt; + } + $bitMask = (1 << $chunkSize) - 1; + $chunks = array(); + while ($int !== 0) { + $chunks[] = $int & $bitMask; + $int >>= $chunkSize; + } + + return new self($negative, array_reverse($chunks), $chunkSize); + } + + /** + * @param string $numericString a string normalized with BinaryMath::normalizeIntegerString() + * @param int $chunkSize + * + * @return \IPLib\Service\NumberInChunks + */ + public static function fromNumericString($numericString, $chunkSize) + { + if ($numericString === '0') { + return new self(false, array(0), $chunkSize); + } + $negative = $numericString[0] === '-'; + if ($negative) { + $numericString = substr($numericString, 1); + } + $chunks = array(); + while ($numericString !== '0') { + $chunks[] = self::modulo($numericString, $chunkSize); + $numericString = self::divide($numericString, $chunkSize); + } + + return new self($negative, array_reverse($chunks), $chunkSize); + } + + /** + * @param string $numericString + * @param int $chunkSize + * + * @return int + */ + private static function modulo($numericString, $chunkSize) + { + $divisor = 1 << $chunkSize; + $carry = 0; + $len = strlen($numericString); + for ($i = 0; $i < $len; $i++) { + $digit = (int) $numericString[$i]; + $carry = ($carry * 10 + $digit) % $divisor; + } + + return $carry; + } + + /** + * @param string $numericString + * @param int $chunkSize + * + * @return string + */ + private static function divide($numericString, $chunkSize) + { + $divisor = 1 << $chunkSize; + $quotient = ''; + $carry = 0; + $len = strlen($numericString); + for ($i = 0; $i < $len; $i++) { + $digit = (int) $numericString[$i]; + $value = $carry * 10 + $digit; + $quotient .= (string) ($value >> $chunkSize); + $carry = $value % $divisor; + } + + return ltrim($quotient, '0') ?: '0'; + } + + /** + * @param int[] $addend1 + * @param int[] $addend2 + * @param int $chunkSize + * + * @return int[] + */ + private static function addChunks(array $addend1, array $addend2, $chunkSize) + { + $divisor = 1 << $chunkSize; + $result = array(); + $carry = 0; + while ($addend1 !== array() || $addend2 !== array()) { + $sum = $carry + (array_pop($addend1) ?: 0) + (array_pop($addend2) ?: 0); + $result[] = $sum % $divisor; + $carry = $sum >> $chunkSize; + } + if ($carry !== 0) { + $result[] = $carry; + } + + return array_reverse($result); + } + + /** + * @param int[] $minuend + * @param int[] $subtrahend + * @param int $chunkSize + * + * @return array + */ + private static function substractChunks(array $minuend, array $subtrahend, $chunkSize) + { + $minuendCount = count($minuend); + $subtrahendCount = count($subtrahend); + if ($minuendCount > $subtrahendCount) { + $count = $minuendCount; + $negative = false; + } elseif ($minuendCount < $subtrahendCount) { + $count = $subtrahendCount; + $negative = true; + } else { + $count = $minuendCount; + $negative = false; + for ($i = 0; $i < $count; $i++) { + $delta = $minuend[$i] - $subtrahend[$i]; + if ($delta === 0) { + continue; + } + if ($delta < 0) { + $negative = true; + } + break; + } + } + if ($negative) { + list($minuend, $subtrahend) = array($subtrahend, $minuend); + } + $subtrahend = array_pad($subtrahend, -$count, 0); + $borrowValue = 1 << $chunkSize; + $result = array(); + $borrow = 0; + for ($i = $count - 1; $i >= 0; $i--) { + $value = $minuend[$i] - $subtrahend[$i] - $borrow; + if ($value < 0) { + $value += $borrowValue; + $borrow = 1; + } else { + $borrow = 0; + } + $result[] = $value; + } + while (isset($result[1])) { + $value = array_pop($result); + if ($value !== 0) { + $result[] = $value; + break; + } + } + + return array($negative, array_reverse($result)); + } +} diff --git a/vendor/mlocati/ip-lib/src/Service/RangesFromBoundaryCalculator.php b/vendor/mlocati/ip-lib/src/Service/RangesFromBoundaryCalculator.php index 7a127e79..f26e94b6 100644 --- a/vendor/mlocati/ip-lib/src/Service/RangesFromBoundaryCalculator.php +++ b/vendor/mlocati/ip-lib/src/Service/RangesFromBoundaryCalculator.php @@ -50,7 +50,7 @@ class RangesFromBoundaryCalculator */ public function __construct($numBits) { - $this->math = new BinaryMath(); + $this->math = BinaryMath::getInstance(); $this->setNumBits($numBits); }