summaryrefslogtreecommitdiff
path: root/vendor/mlocati
diff options
context:
space:
mode:
authorGreg Roach <fisharebest@webtrees.net>2020-02-13 16:40:38 +0000
committerGreg Roach <fisharebest@webtrees.net>2020-02-17 15:16:43 +0000
commit089dadac9f029a2c2e920aab5c38c35029818ae2 (patch)
tree15e9e02ee6fb4ead8f1aa1e69643fae293d441f3 /vendor/mlocati
parentf376f6d21758a47a239124a2664a6bef0fbcc699 (diff)
downloadwebtrees-089dadac9f029a2c2e920aab5c38c35029818ae2.tar.gz
webtrees-089dadac9f029a2c2e920aab5c38c35029818ae2.tar.bz2
webtrees-089dadac9f029a2c2e920aab5c38c35029818ae2.zip
Check for bad robots
Diffstat (limited to 'vendor/mlocati')
-rw-r--r--vendor/mlocati/ip-lib/LICENSE.txt20
-rw-r--r--vendor/mlocati/ip-lib/README.md311
-rw-r--r--vendor/mlocati/ip-lib/composer.json48
-rw-r--r--vendor/mlocati/ip-lib/ip-lib.php13
-rw-r--r--vendor/mlocati/ip-lib/src/Address/AddressInterface.php96
-rw-r--r--vendor/mlocati/ip-lib/src/Address/AssignedRange.php138
-rw-r--r--vendor/mlocati/ip-lib/src/Address/IPv4.php352
-rw-r--r--vendor/mlocati/ip-lib/src/Address/IPv6.php526
-rw-r--r--vendor/mlocati/ip-lib/src/Address/Type.php42
-rw-r--r--vendor/mlocati/ip-lib/src/Factory.php156
-rw-r--r--vendor/mlocati/ip-lib/src/Range/Pattern.php331
-rw-r--r--vendor/mlocati/ip-lib/src/Range/RangeInterface.php96
-rw-r--r--vendor/mlocati/ip-lib/src/Range/Single.php190
-rw-r--r--vendor/mlocati/ip-lib/src/Range/Subnet.php334
-rw-r--r--vendor/mlocati/ip-lib/src/Range/Type.php141
15 files changed, 2794 insertions, 0 deletions
diff --git a/vendor/mlocati/ip-lib/LICENSE.txt b/vendor/mlocati/ip-lib/LICENSE.txt
new file mode 100644
index 0000000000..5c21e47cf5
--- /dev/null
+++ b/vendor/mlocati/ip-lib/LICENSE.txt
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+Copyright (c) 2016 Michele Locati
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE. \ No newline at end of file
diff --git a/vendor/mlocati/ip-lib/README.md b/vendor/mlocati/ip-lib/README.md
new file mode 100644
index 0000000000..729cd4c255
--- /dev/null
+++ b/vendor/mlocati/ip-lib/README.md
@@ -0,0 +1,311 @@
+[![TravisCI Build Status](https://api.travis-ci.org/mlocati/ip-lib.svg?branch=master)](https://travis-ci.org/mlocati/ip-lib)
+[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/mlocati/ip-lib?branch=master&svg=true)](https://ci.appveyor.com/project/mlocati/ip-lib)
+[![StyleCI Status](https://styleci.io/repos/54139375/shield)](https://styleci.io/repos/54139375)
+[![Coverage Status](https://coveralls.io/repos/github/mlocati/ip-lib/badge.svg?branch=master)](https://coveralls.io/github/mlocati/ip-lib?branch=master)
+[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/mlocati/ip-lib/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/mlocati/ip-lib/?branch=master)
+
+# IPLib - Handle IPv4, IPv6 and IP ranges
+
+
+## Introduction
+
+This library can handle IPv4, IPv6 addresses, as well as IP ranges, in CIDR formats (like `::1/128` or `127.0.0.1/32`) and in pattern format (like `::*:*` or `127.0.*.*`).
+
+
+## Requirements
+
+The only requirement is PHP 5.3.3.
+__No external dependencies__ and __no special PHP configuration__ are needed (yes, it will __always work__ even if PHP has not been built with IPv6 support!).
+
+
+## Manual installation
+
+[Download](https://github.com/mlocati/ip-lib/releases) the latest version, unzip it and add these lines in our PHP files:
+
+```php
+require_once 'path/to/iplib/ip-lib.php';
+```
+
+
+## Installation with Composer
+
+Simply run `composer require mlocati/ip-lib`, or add these lines to your `composer.json` file:
+
+```json
+"require": {
+ "mlocati/ip-lib": "1.*"
+}
+```
+
+
+## Sample usage
+
+
+### Parse an address
+
+To parse an IPv4 address:
+
+```php
+$address = \IPLib\Address\IPv4::fromString('127.0.0.1');
+```
+
+To parse an IPv6 address:
+
+```php
+$address = \IPLib\Address\IPv6::fromString('::1');
+```
+
+To parse an address in any format (IPv4 or IPv6):
+
+```php
+$address = \IPLib\Factory::addressFromString('::1');
+$address = \IPLib\Factory::addressFromString('127.0.0.1');
+```
+
+
+### Get the next/previous addresses
+
+```php
+$address = \IPLib\Factory::addressFromString('::1');
+echo (string) $address->getPreviousAddress();
+// prints ::
+echo (string) $address->getNextAddress();
+// prints ::2
+```
+
+
+### Parse an IP address range
+
+To parse a subnet (CIDR) range:
+
+```php
+$range = \IPLib\Range\Subnet::fromString('127.0.0.1/24');
+$range = \IPLib\Range\Subnet::fromString('::1/128');
+```
+
+To parse a pattern (asterisk notation) range:
+
+```php
+$range = \IPLib\Range\Pattern::fromString('127.0.0.*');
+$range = \IPLib\Range\Pattern::fromString('::*');
+```
+
+To parse an andress as a range:
+
+```php
+$range = \IPLib\Range\Single::fromString('127.0.0.1');
+$range = \IPLib\Range\Single::fromString('::1');
+```
+
+To parse a range in any format:
+
+```php
+$range = \IPLib\Factory::rangeFromString('127.0.0.*');
+$range = \IPLib\Factory::rangeFromString('::1/128');
+$range = \IPLib\Factory::rangeFromString('::');
+```
+
+
+### Retrive a range from its boundaries
+
+```php
+$range = \IPLib\Factory::rangeFromBoundaries('192.168.0.1', '192.168.255.255');
+echo (string) $range;
+// prints 192.168.0.0/16
+```
+
+
+### Retrive the boundaries of a range
+
+```php
+$range = \IPLib\Factory::rangeFromString('127.0.0.*');
+echo (string) $range->getStartAddress();
+// prints 127.0.0.0
+echo (string) $range->getEndAddress();
+// prints 127.0.0.255
+```
+
+
+### Format addresses and ranges
+
+Both IP addresses and ranges have a `toString` method that you can use to retrieve a textual representation:
+
+```php
+echo \IPLib\Factory::addressFromString('127.0.0.1')->toString();
+// prints 127.0.0.1
+echo \IPLib\Factory::addressFromString('127.000.000.001')->toString();
+// prints 127.0.0.1
+echo \IPLib\Factory::addressFromString('::1')->toString();
+// prints ::1
+echo \IPLib\Factory::addressFromString('0:0::1')->toString();
+// prints ::1
+echo \IPLib\Factory::rangeFromString('0:0::1/64')->toString();
+// prints ::1/64
+```
+
+When working with IPv6, you may want the full (expanded) representation of the addresses. In this case, simply use a `true` parameter for the `toString` method:
+
+```php
+echo \IPLib\Factory::addressFromString('::')->toString(true);
+// prints 0000:0000:0000:0000:0000:0000:0000:0000
+echo \IPLib\Factory::addressFromString('::1')->toString(true);
+// prints 0000:0000:0000:0000:0000:0000:0000:0001
+echo \IPLib\Factory::addressFromString('fff::')->toString(true);
+// prints 0fff:0000:0000:0000:0000:0000:0000:0000
+echo \IPLib\Factory::addressFromString('::0:0')->toString(true);
+// prints 0000:0000:0000:0000:0000:0000:0000:0000
+echo \IPLib\Factory::addressFromString('1:2:3:4:5:6:7:8')->toString(true);
+// prints 0001:0002:0003:0004:0005:0006:0007:0008
+echo \IPLib\Factory::rangeFromString('0:0::1/64')->toString();
+// prints 0000:0000:0000:0000:0000:0000:0000:0001/64
+```
+
+
+### Check if an address is contained in a range
+
+All the range types offer a `contains` method, and all the IP address types offer a `matches` method: you can call them to check if an address is contained in a range:
+
+```php
+$address = \IPLib\Factory::addressFromString('1:2:3:4:5:6:7:8');
+$range = \IPLib\Factory::rangeFromString('0:0::1/64');
+
+$contained = $address->matches($range);
+// that's equivalent to
+$contained = $range->contains($address);
+```
+
+Please remark that if the address is IPv4 and the range is IPv6 (or vice-versa), the result will always be `false`.
+
+
+### Check if a range contains another range
+
+All the range types offer a `containsRange` method: you can call them to check if an address range fully contains another range:
+
+```php
+$range1 = \IPLib\Factory::rangeFromString('0:0::1/64');
+$range2 = \IPLib\Factory::rangeFromString('0:0::1/65');
+$contained = $range1->containsRange($range2);
+```
+
+
+### Getting the type of an IP address
+
+If you want to know if an address is within a private network, or if it's a public IP, or whatever you want, you can use the `getRangeType` method:
+
+```php
+$address = \IPLib\Factory::addressFromString('::');
+
+$typeID = $address->getRangeType();
+
+$typeName = \IPLib\Range\Type::getName();
+```
+
+The most notable values of the range type ID are:
+- `\IPLib\Range\Type::T_UNSPECIFIED` if the address is all zeros (`0.0.0.0` or `::`)
+- `\IPLib\Range\Type::T_LOOPBACK` if the address is the localhost (usually `127.0.0.1` or `::1`)
+- `\IPLib\Range\Type::T_PRIVATENETWORK` if the address is in the local network (for instance `192.168.0.1` or `fc00::1`)
+- `\IPLib\Range\Type::T_PUBLIC` if the address is for public usage (for instance `104.25.25.33` or `2001:503:ba3e::2:30`)
+
+
+### Getting the type of an IP address range
+
+If you want to know the type of an address range, you can use the `getRangeType` method:
+
+```php
+$range = \IPLib\Factory::rangeFromString('2000:0::1/64');
+$type = $range->getRangeType();
+// $type is \IPLib\Range\Type::T_PUBLIC
+echo \IPLib\Range\Type::getName($type);
+// 'Public address'
+```
+
+Please remark that if a range spans across multiple range types, you'll get NULL as the range type:
+
+```php
+$range = \IPLib\Factory::rangeFromString('::/127');
+$type = $range->getRangeType();
+// $type is null
+echo \IPLib\Range\Type::getName($type);
+// 'Unknown type'
+```
+
+### Converting IP ranges
+
+This library supports IPv4/IPv6 ranges in pattern format (eg. `192.168.*.*`) and in CIDR/subnet format (eg. `192.168.0.0/16`), and it offers a way to convert between the two formats:
+
+```php
+// This will print ::*:*:*:*
+echo \IPLib\Factory::rangeFromString('::/64')->asPattern()->toString();
+
+// This will print 1:2::/96
+echo \IPLib\Factory::rangeFromString('1:2::*:*')->asSubnet()->toString();
+
+// This will print 192.168.0.0/24
+echo \IPLib\Factory::rangeFromString('192.168.0.*')->asSubnet()->toString();
+
+// This will print 10.*.*.*
+echo \IPLib\Factory::rangeFromString('10.0.0.0/8')->asPattern()->toString();
+```
+
+### Getting the subnet mask for IPv4 ranges
+
+You can use the `getSubnetMask()` to get the subnet mask for IPv4 ranges:
+
+```php
+// This will print 255.255.255.0
+echo \IPLib\Factory::rangeFromString('192.168.0.*')->getSubnetMask()->toString();
+
+// This will print 255.255.255.252
+echo \IPLib\Factory::rangeFromString('192.168.0.12/30')->getSubnetMask()->toString();
+```
+
+### Using a database
+
+This package offers a great feature: you can store address ranges in a database table, and check if an address is contained in one of the saved ranges with a simple query.
+
+To save a range, you need to store the address type (for IPv4 it's `4`, for IPv6 it's `6`), as well as two values representing the start and the end of the range.
+These methods are:
+```php
+$range->getAddressType();
+$range->getComparableStartString();
+$range->getComparableEndString();
+```
+
+Let's assume that you saved the type in a field called `addressType`, and the range boundaries in two fields called `rangeFrom` and `rangeTo`.
+
+When you want to check if an address is within a stored range, simply use the `getComparableString` method of the address and check if it's between the fields `rangeFrom` and `rangeTo`, and check if the stored `addressType` is the same as the one of the address instance you want to check.
+
+Here's a sample code:
+
+```php
+/*
+ * Let's assume that:
+ * - $pdo is a PDO instance
+ * - $range is a range object
+ * - $address is an address object
+ */
+
+// Save the $range object
+$insertQuery = $pdo->prepare('
+ insert into ranges (addressType, rangeFrom, rangeTo)
+ values (:addressType, :rangeFrom, :rangeTo)
+');
+$insertQuery->execute(array(
+ ':addressType' => $range->getAddressType(),
+ ':rangeFrom' => $range->getComparableStartString(),
+ ':rangeTo' => $range->getComparableEndString(),
+));
+
+// Retrieve the saved ranges where an address $address falls:
+$searchQuery = $pdo->prepare('
+ select * from ranges
+ where addressType = :addressType
+ and :address between rangeFrom and rangeTo
+');
+$searchQuery->execute(array(
+ ':addressType' => $address->getAddressType(),
+ ':address' => $address->getComparableString(),
+));
+$rows = $searchQuery->fetchAll();
+$searchQuery->closeCursor();
+```
diff --git a/vendor/mlocati/ip-lib/composer.json b/vendor/mlocati/ip-lib/composer.json
new file mode 100644
index 0000000000..579147c760
--- /dev/null
+++ b/vendor/mlocati/ip-lib/composer.json
@@ -0,0 +1,48 @@
+{
+ "name": "mlocati/ip-lib",
+ "type": "library",
+ "description": "Handle IPv4, IPv6 addresses and ranges",
+ "keywords": [
+ "ip",
+ "ipv4",
+ "ipv6",
+ "range",
+ "network",
+ "networking",
+ "address",
+ "addresses",
+ "subnet",
+ "matching"
+ ],
+ "homepage": "https://github.com/mlocati/ip-lib",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Michele Locati",
+ "homepage": "https://github.com/mlocati",
+ "email": "mlocati@gmail.com",
+ "role": "Author"
+ }
+ ],
+ "autoload": {
+ "psr-4": {
+ "IPLib\\": "src/"
+ }
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "IPLib\\Test\\": "test/tests/"
+ }
+ },
+ "require-dev": {
+ "ext-pdo_sqlite": "*",
+ "phpunit/phpunit": "^4.8 || ^5.7 || ^6.5",
+ "phpunit/dbunit": "^1.4 || ^2 || ^3 || ^4"
+ },
+ "scripts": {
+ "test": "phpunit"
+ }
+}
diff --git a/vendor/mlocati/ip-lib/ip-lib.php b/vendor/mlocati/ip-lib/ip-lib.php
new file mode 100644
index 0000000000..c1590ef067
--- /dev/null
+++ b/vendor/mlocati/ip-lib/ip-lib.php
@@ -0,0 +1,13 @@
+<?php
+
+spl_autoload_register(
+ function ($class) {
+ if (strpos($class, 'IPLib\\') !== 0) {
+ return;
+ }
+ $file = __DIR__.DIRECTORY_SEPARATOR.'src'.str_replace('\\', DIRECTORY_SEPARATOR, substr($class, strlen('IPLib'))).'.php';
+ if (is_file($file)) {
+ require_once $file;
+ }
+ }
+);
diff --git a/vendor/mlocati/ip-lib/src/Address/AddressInterface.php b/vendor/mlocati/ip-lib/src/Address/AddressInterface.php
new file mode 100644
index 0000000000..1a73f21f8e
--- /dev/null
+++ b/vendor/mlocati/ip-lib/src/Address/AddressInterface.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace IPLib\Address;
+
+use IPLib\Range\RangeInterface;
+
+/**
+ * Interface of all the IP address types.
+ */
+interface AddressInterface
+{
+ /**
+ * Get the string representation of this address.
+ *
+ * @param bool $long set to true to have a long/full representation, false otherwise
+ *
+ * @return string
+ *
+ * @example If $long is true, you'll get '0000:0000:0000:0000:0000:0000:0000:0001', '::1' otherwise.
+ */
+ public function toString($long = false);
+
+ /**
+ * Get the short string representation of this address.
+ *
+ * @return string
+ */
+ public function __toString();
+
+ /**
+ * Get the byte list of the IP address.
+ *
+ * @return int[]
+ *
+ * @example For IPv4 you'll get array(127, 0, 0, 1), for IPv6 array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1)
+ */
+ public function getBytes();
+
+ /**
+ * Get the type of the IP address.
+ *
+ * @return int One of the \IPLib\Address\Type::T_... constants
+ */
+ public function getAddressType();
+
+ /**
+ * Get the default RFC reserved range type.
+ *
+ * @return int One of the \IPLib\Range\Type::T_... constants
+ */
+ public static function getDefaultReservedRangeType();
+
+ /**
+ * Get the RFC reserved ranges (except the ones of type getDefaultReservedRangeType).
+ *
+ * @return \IPLib\Address\AssignedRange[] ranges are sorted
+ */
+ public static function getReservedRanges();
+
+ /**
+ * Get the type of range of the IP address.
+ *
+ * @return int One of the \IPLib\Range\Type::T_... constants
+ */
+ public function getRangeType();
+
+ /**
+ * Get a string representation of this address than can be used when comparing addresses and ranges.
+ *
+ * @return string
+ */
+ public function getComparableString();
+
+ /**
+ * Check if this address is contained in an range.
+ *
+ * @param \IPLib\Range\RangeInterface $range
+ *
+ * @return bool
+ */
+ public function matches(RangeInterface $range);
+
+ /**
+ * Get the address right after this IP address (if available).
+ *
+ * @return \IPLib\Address\AddressInterface|null
+ */
+ public function getNextAddress();
+
+ /**
+ * Get the address right before this IP address (if available).
+ *
+ * @return \IPLib\Address\AddressInterface|null
+ */
+ public function getPreviousAddress();
+}
diff --git a/vendor/mlocati/ip-lib/src/Address/AssignedRange.php b/vendor/mlocati/ip-lib/src/Address/AssignedRange.php
new file mode 100644
index 0000000000..39dfec63d4
--- /dev/null
+++ b/vendor/mlocati/ip-lib/src/Address/AssignedRange.php
@@ -0,0 +1,138 @@
+<?php
+
+namespace IPLib\Address;
+
+use IPLib\Range\RangeInterface;
+
+/**
+ * Represents an IP address range with an assigned range type.
+ */
+class AssignedRange
+{
+ /**
+ * The range definition.
+ *
+ * @var \IPLib\Range\RangeInterface
+ */
+ protected $range;
+
+ /**
+ * The range type.
+ *
+ * @var int one of the \IPLib\Range\Type::T_ constants
+ */
+ protected $type;
+
+ /**
+ * The list of exceptions for this range type.
+ *
+ * @var \IPLib\Address\AssignedRange[]
+ */
+ protected $exceptions;
+
+ /**
+ * Initialize the instance.
+ *
+ * @param \IPLib\Range\RangeInterface $range the range definition
+ * @param int $type The range type (one of the \IPLib\Range\Type::T_ constants)
+ * @param \IPLib\Address\AssignedRange[] $exceptions the list of exceptions for this range type
+ */
+ public function __construct(RangeInterface $range, $type, array $exceptions = array())
+ {
+ $this->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|null|false 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 0000000000..272dc1d08f
--- /dev/null
+++ b/vendor/mlocati/ip-lib/src/Address/IPv4.php
@@ -0,0 +1,352 @@
+<?php
+
+namespace IPLib\Address;
+
+use IPLib\Range\RangeInterface;
+use IPLib\Range\Subnet;
+use IPLib\Range\Type as RangeType;
+
+/**
+ * An IPv4 address.
+ */
+class IPv4 implements AddressInterface
+{
+ /**
+ * The string representation of the address.
+ *
+ * @var string
+ *
+ * @example '127.0.0.1'
+ */
+ protected $address;
+
+ /**
+ * The byte list of the IP address.
+ *
+ * @var int[]|null
+ */
+ protected $bytes;
+
+ /**
+ * The type of the range of this IP address.
+ *
+ * @var int|null
+ */
+ protected $rangeType;
+
+ /**
+ * A string representation of this address than can be used when comparing addresses and ranges.
+ *
+ * @var string
+ */
+ protected $comparableString;
+
+ /**
+ * An array containing RFC designated address ranges.
+ *
+ * @var array|null
+ */
+ private static $reservedRanges = null;
+
+ /**
+ * Initializes the instance.
+ *
+ * @param string $address
+ */
+ protected function __construct($address)
+ {
+ $this->address = $address;
+ $this->bytes = null;
+ $this->rangeType = null;
+ $this->comparableString = null;
+ }
+
+ /**
+ * 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
+ *
+ * @return static|null
+ */
+ public static function fromString($address, $mayIncludePort = true)
+ {
+ $result = null;
+ if (is_string($address) && strpos($address, '.')) {
+ $rx = '([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})';
+ if ($mayIncludePort) {
+ $rx .= '(?::\d+)?';
+ }
+ $matches = null;
+ if (preg_match('/^'.$rx.'$/', $address, $matches)) {
+ $ok = true;
+ $nums = array();
+ for ($i = 1; $ok && $i <= 4; ++$i) {
+ $ok = false;
+ $n = (int) $matches[$i];
+ if ($n >= 0 && $n <= 255) {
+ $ok = true;
+ $nums[] = (string) $n;
+ }
+ }
+ if ($ok) {
+ $result = new static(implode('.', $nums));
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see \IPLib\Address\AddressInterface::__toString()
+ */
+ public function __toString()
+ {
+ return $this->address;
+ }
+
+ /**
+ * {@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::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 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.
+ *
+ * @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]).'::');
+ }
+
+ /**
+ * {@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);
+ }
+}
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 0000000000..170af8bb50
--- /dev/null
+++ b/vendor/mlocati/ip-lib/src/Address/IPv6.php
@@ -0,0 +1,526 @@
+<?php
+
+namespace IPLib\Address;
+
+use IPLib\Range\RangeInterface;
+use IPLib\Range\Subnet;
+use IPLib\Range\Type as RangeType;
+
+/**
+ * An IPv6 address.
+ */
+class IPv6 implements AddressInterface
+{
+ /**
+ * The long string representation of the address.
+ *
+ * @var string
+ *
+ * @example '0000:0000:0000:0000:0000:0000:0000:0001'
+ */
+ protected $longAddress;
+
+ /**
+ * The long string representation of the address.
+ *
+ * @var string|null
+ *
+ * @example '::1'
+ */
+ protected $shortAddress;
+
+ /**
+ * The byte list of the IP address.
+ *
+ * @var int[]|null
+ */
+ protected $bytes;
+
+ /**
+ * The word list of the IP address.
+ *
+ * @var int[]|null
+ */
+ protected $words;
+
+ /**
+ * The type of the range of this IP address.
+ *
+ * @var int|null
+ */
+ protected $rangeType;
+
+ /**
+ * An array containing RFC designated address ranges.
+ *
+ * @var array|null
+ */
+ private static $reservedRanges = null;
+
+ /**
+ * Initializes the instance.
+ *
+ * @param string $longAddress
+ */
+ public function __construct($longAddress)
+ {
+ $this->longAddress = $longAddress;
+ $this->shortAddress = null;
+ $this->bytes = null;
+ $this->words = null;
+ $this->rangeType = null;
+ }
+
+ /**
+ * 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::__toString()
+ */
+ public function __toString()
+ {
+ return $this->toString();
+ }
+
+ /**
+ * {@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;
+ }
+
+ /**
+ * 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()
+ {
+ $result = null;
+ if (strpos($this->longAddress, '2002:') === 0) {
+ $result = IPv4::fromBytes(array_slice($this->getBytes(), 2, 4));
+ }
+
+ return $result;
+ }
+
+ /**
+ * 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);
+ }
+}
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 0000000000..06b07535d7
--- /dev/null
+++ b/vendor/mlocati/ip-lib/src/Address/Type.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace IPLib\Address;
+
+/**
+ * Types of IP addresses.
+ */
+class Type
+{
+ /**
+ * IPv4 address.
+ *
+ * @var int
+ */
+ const T_IPv4 = 4;
+
+ /**
+ * IPv6 address.
+ *
+ * @var int
+ */
+ const T_IPv6 = 6;
+
+ /**
+ * Get the name of a type.
+ *
+ * @param int $type
+ *
+ * @return string
+ */
+ public static function getName($type)
+ {
+ switch ($type) {
+ case static::T_IPv4:
+ return 'IP v4';
+ case static::T_IPv6:
+ return 'IP v6';
+ default:
+ return sprintf('Unknown type (%s)', $type);
+ }
+ }
+}
diff --git a/vendor/mlocati/ip-lib/src/Factory.php b/vendor/mlocati/ip-lib/src/Factory.php
new file mode 100644
index 0000000000..947655998b
--- /dev/null
+++ b/vendor/mlocati/ip-lib/src/Factory.php
@@ -0,0 +1,156 @@
+<?php
+
+namespace IPLib;
+
+use IPLib\Address\AddressInterface;
+
+/**
+ * Factory methods to build class instances.
+ */
+class Factory
+{
+ /**
+ * Parse an IP address string.
+ *
+ * @param string $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 IPv6 addresses with zone IDs (see RFC 4007)
+ *
+ * @return \IPLib\Address\AddressInterface|null
+ */
+ public static function addressFromString($address, $mayIncludePort = true, $mayIncludeZoneID = true)
+ {
+ $result = null;
+ if ($result === null) {
+ $result = Address\IPv4::fromString($address, $mayIncludePort);
+ }
+ if ($result === null) {
+ $result = Address\IPv6::fromString($address, $mayIncludePort, $mayIncludeZoneID);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Convert a byte array to an address instance.
+ *
+ * @param int[]|array $bytes
+ *
+ * @return \IPLib\Address\AddressInterface|null
+ */
+ public static function addressFromBytes(array $bytes)
+ {
+ $result = null;
+ if ($result === null) {
+ $result = Address\IPv4::fromBytes($bytes);
+ }
+ if ($result === null) {
+ $result = Address\IPv6::fromBytes($bytes);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Parse an IP range string.
+ *
+ * @param string $range
+ *
+ * @return \IPLib\Range\RangeInterface|null
+ */
+ public static function rangeFromString($range)
+ {
+ $result = null;
+ if ($result === null) {
+ $result = Range\Subnet::fromString($range);
+ }
+ if ($result === null) {
+ $result = Range\Pattern::fromString($range);
+ }
+ if ($result === null) {
+ $result = Range\Single::fromString($range);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Create a Range instance starting from its boundaries.
+ *
+ * @param string|\IPLib\Address\AddressInterface $from
+ * @param string|\IPLib\Address\AddressInterface $to
+ *
+ * @return \IPLib\Range\RangeInterface|null
+ */
+ public static function rangeFromBoundaries($from, $to)
+ {
+ $result = null;
+ $invalid = false;
+ foreach (array('from', 'to') as $param) {
+ if (!($$param instanceof AddressInterface)) {
+ $$param = (string) $$param;
+ if ($$param === '') {
+ $$param = null;
+ } else {
+ $$param = static::addressFromString($$param);
+ if ($$param === null) {
+ $invalid = true;
+ }
+ }
+ }
+ }
+ if ($invalid === false) {
+ $result = static::rangeFromBoundaryAddresses($from, $to);
+ }
+
+ return $result;
+ }
+
+ /**
+ * @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;
+ }
+}
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 0000000000..32a7864519
--- /dev/null
+++ b/vendor/mlocati/ip-lib/src/Range/Pattern.php
@@ -0,0 +1,331 @@
+<?php
+
+namespace IPLib\Range;
+
+use IPLib\Address\AddressInterface;
+use IPLib\Address\IPv4;
+use IPLib\Address\IPv6;
+use IPLib\Address\Type as AddressType;
+use IPLib\Factory;
+
+/**
+ * Represents an address range in pattern format (only ending asterisks are supported).
+ *
+ * @example 127.0.*.*
+ * @example ::/8
+ */
+class Pattern implements RangeInterface
+{
+ /**
+ * Starting address of the range.
+ *
+ * @var \IPLib\Address\AddressInterface
+ */
+ protected $fromAddress;
+
+ /**
+ * Final address of the range.
+ *
+ * @var \IPLib\Address\AddressInterface
+ */
+ protected $toAddress;
+
+ /**
+ * Number of ending asterisks.
+ *
+ * @var int
+ */
+ protected $asterisksCount;
+
+ /**
+ * The type of the range of this IP range.
+ *
+ * @var int|null|false false if this range crosses multiple range types, null if yet to be determined
+ */
+ protected $rangeType;
+
+ /**
+ * Initializes the instance.
+ *
+ * @param \IPLib\Address\AddressInterface $fromAddress
+ * @param \IPLib\Address\AddressInterface $toAddress
+ * @param int $asterisksCount
+ */
+ public function __construct(AddressInterface $fromAddress, AddressInterface $toAddress, $asterisksCount)
+ {
+ $this->fromAddress = $fromAddress;
+ $this->toAddress = $toAddress;
+ $this->asterisksCount = $asterisksCount;
+ }
+
+ /**
+ * Try get the range instance starting from its string representation.
+ *
+ * @param string|mixed $range
+ *
+ * @return static|null
+ */
+ public static function fromString($range)
+ {
+ $result = null;
+ if (is_string($range) && strpos($range, '*') !== false) {
+ $matches = null;
+ if ($range === '*.*.*.*') {
+ $result = new static(IPv4::fromString('0.0.0.0'), IPv4::fromString('255.255.255.255'), 4);
+ } elseif (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));
+ if ($fromAddress !== null) {
+ $fixedBytes = array_slice($fromAddress->getBytes(), 0, -$asterisksCount);
+ $otherBytes = array_fill(0, $asterisksCount, 255);
+ $toAddress = IPv4::fromBytes(array_merge($fixedBytes, $otherBytes));
+ $result = new static($fromAddress, $toAddress, $asterisksCount);
+ }
+ } elseif ($range === '*:*:*:*:*:*:*:*') {
+ $result = new static(IPv6::fromString('::'), IPv6::fromString('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'), 8);
+ } elseif (strpos($range, ':') !== false && preg_match('/^[^*]+((?::\*)+)$/', $range, $matches)) {
+ $asterisksCount = strlen($matches[1]) >> 1;
+ $fromAddress = IPv6::fromString(str_replace('*', '0', $range));
+ if ($fromAddress !== null) {
+ $fixedWords = array_slice($fromAddress->getWords(), 0, -$asterisksCount);
+ $otherWords = array_fill(0, $asterisksCount, 0xffff);
+ $toAddress = IPv6::fromWords(array_merge($fixedWords, $otherWords));
+ $result = new static($fromAddress, $toAddress, $asterisksCount);
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * {@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::__toString()
+ */
+ public function __toString()
+ {
+ return $this->toString();
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see \IPLib\Range\RangeInterface::getAddressType()
+ */
+ public function getAddressType()
+ {
+ return $this->fromAddress->getAddressType();
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see \IPLib\Range\RangeInterface::getRangeType()
+ */
+ public function getRangeType()
+ {
+ if ($this->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;
+ }
+
+ /**
+ * {@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();
+ }
+
+ /**
+ * Get the subnet/CIDR representation of this range.
+ *
+ * @return \IPLib\Range\Subnet
+ */
+ 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::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);
+ }
+}
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 0000000000..1ddd9d6878
--- /dev/null
+++ b/vendor/mlocati/ip-lib/src/Range/RangeInterface.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace IPLib\Range;
+
+use IPLib\Address\AddressInterface;
+
+/**
+ * Interface of all the range types.
+ */
+interface RangeInterface
+{
+ /**
+ * Get the string representation of this address.
+ *
+ * @param bool $long set to true to have a long/full representation, false otherwise
+ *
+ * @return string
+ *
+ * @example If $long is true, you'll get '0000:0000:0000:0000:0000:0000:0000:0001/128', '::1/128' otherwise.
+ */
+ public function toString($long = false);
+
+ /**
+ * Get the short string representation of this address.
+ *
+ * @return string
+ */
+ public function __toString();
+
+ /**
+ * Get the type of the IP addresses contained in this range.
+ *
+ * @return int One of the \IPLib\Address\Type::T_... constants
+ */
+ public function getAddressType();
+
+ /**
+ * Get the type of range of the IP address.
+ *
+ * @return int One of the \IPLib\Range\Type::T_... constants
+ */
+ public function getRangeType();
+
+ /**
+ * Check if this range contains an IP address.
+ *
+ * @param \IPLib\Address\AddressInterface $address
+ *
+ * @return bool
+ */
+ public function contains(AddressInterface $address);
+
+ /**
+ * Check if this range contains another range.
+ *
+ * @param \IPLib\Range\RangeInterface $range
+ *
+ * @return bool
+ */
+ public function containsRange(RangeInterface $range);
+
+ /**
+ * Get the initial address contained in this range.
+ *
+ * @return \IPLib\Address\AddressInterface
+ */
+ public function getStartAddress();
+
+ /**
+ * Get the final address contained in this range.
+ *
+ * @return \IPLib\Address\AddressInterface
+ */
+ public function getEndAddress();
+
+ /**
+ * Get a string representation of the starting address of this range than can be used when comparing addresses and ranges.
+ *
+ * @return string
+ */
+ public function getComparableStartString();
+
+ /**
+ * Get a string representation of the final address of this range than can be used when comparing addresses and ranges.
+ *
+ * @return string
+ */
+ public function getComparableEndString();
+
+ /**
+ * Get the subnet mask representing this range (only for IPv4 ranges).
+ *
+ * @return \IPLib\Address\IPv4|null return NULL if the range is an IPv6 range, the subnet mask otherwise
+ */
+ public function getSubnetMask();
+}
diff --git a/vendor/mlocati/ip-lib/src/Range/Single.php b/vendor/mlocati/ip-lib/src/Range/Single.php
new file mode 100644
index 0000000000..5bbd437838
--- /dev/null
+++ b/vendor/mlocati/ip-lib/src/Range/Single.php
@@ -0,0 +1,190 @@
+<?php
+
+namespace IPLib\Range;
+
+use IPLib\Address\AddressInterface;
+use IPLib\Address\IPv4;
+use IPLib\Address\Type as AddressType;
+use IPLib\Factory;
+
+/**
+ * Represents a single address (eg a range that contains just one address).
+ *
+ * @example 127.0.0.1
+ * @example ::1
+ */
+class Single implements RangeInterface
+{
+ /**
+ * @var \IPLib\Address\AddressInterface
+ */
+ protected $address;
+
+ /**
+ * Initializes the instance.
+ *
+ * @param \IPLib\Address\AddressInterface $address
+ */
+ protected function __construct(AddressInterface $address)
+ {
+ $this->address = $address;
+ }
+
+ /**
+ * Try get the range instance starting from its string representation.
+ *
+ * @param string|mixed $range
+ *
+ * @return static|null
+ */
+ public static function fromString($range)
+ {
+ $result = null;
+ $address = Factory::addressFromString($range);
+ 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::__toString()
+ */
+ public function __toString()
+ {
+ return $this->address->__toString();
+ }
+
+ /**
+ * {@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::getSubnetMask()
+ */
+ public function getSubnetMask()
+ {
+ if ($this->getAddressType() !== AddressType::T_IPv4) {
+ return null;
+ }
+
+ return IPv4::fromBytes(array(255, 255, 255, 255));
+ }
+}
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 0000000000..6f4aebdebc
--- /dev/null
+++ b/vendor/mlocati/ip-lib/src/Range/Subnet.php
@@ -0,0 +1,334 @@
+<?php
+
+namespace IPLib\Range;
+
+use IPLib\Address\AddressInterface;
+use IPLib\Address\IPv4;
+use IPLib\Address\IPv6;
+use IPLib\Address\Type as AddressType;
+use IPLib\Factory;
+
+/**
+ * Represents an address range in subnet format (eg CIDR).
+ *
+ * @example 127.0.0.1/32
+ * @example ::/8
+ */
+class Subnet implements RangeInterface
+{
+ /**
+ * Starting address of the range.
+ *
+ * @var \IPLib\Address\AddressInterface
+ */
+ protected $fromAddress;
+
+ /**
+ * Final address of the range.
+ *
+ * @var \IPLib\Address\AddressInterface
+ */
+ protected $toAddress;
+
+ /**
+ * Number of the same bits of the range.
+ *
+ * @var int
+ */
+ protected $networkPrefix;
+
+ /**
+ * The type of the range of this IP range.
+ *
+ * @var int|null
+ */
+ protected $rangeType;
+
+ /**
+ * The 6to4 address IPv6 address range.
+ *
+ * @var self|null
+ */
+ private static $sixToFour;
+
+ /**
+ * Initializes the instance.
+ *
+ * @param \IPLib\Address\AddressInterface $fromAddress
+ * @param \IPLib\Address\AddressInterface $toAddress
+ * @param int $networkPrefix
+ *
+ * @internal
+ */
+ public function __construct(AddressInterface $fromAddress, AddressInterface $toAddress, $networkPrefix)
+ {
+ $this->fromAddress = $fromAddress;
+ $this->toAddress = $toAddress;
+ $this->networkPrefix = $networkPrefix;
+ }
+
+ /**
+ * Try get the range instance starting from its string representation.
+ *
+ * @param string|mixed $range
+ *
+ * @return static|null
+ */
+ public static function fromString($range)
+ {
+ $result = null;
+ if (is_string($range)) {
+ $parts = explode('/', $range);
+ if (count($parts) === 2) {
+ $address = Factory::addressFromString($parts[0]);
+ if ($address !== null) {
+ if (preg_match('/^[0-9]{1,9}$/', $parts[1])) {
+ $networkPrefix = (int) $parts[1];
+ if ($networkPrefix >= 0) {
+ $addressBytes = $address->getBytes();
+ $totalBytes = count($addressBytes);
+ $numDifferentBits = $totalBytes * 8 - $networkPrefix;
+ if ($numDifferentBits >= 0) {
+ $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));
+ }
+ $result = new static(
+ Factory::addressFromBytes(array_merge($sameBytes, $differentBytesStart)),
+ Factory::addressFromBytes(array_merge($sameBytes, $differentBytesEnd)),
+ $networkPrefix
+ );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see \IPLib\Range\RangeInterface::toString()
+ */
+ public function toString($long = false)
+ {
+ return $this->fromAddress->toString($long).'/'.$this->networkPrefix;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see \IPLib\Range\RangeInterface::__toString()
+ */
+ public function __toString()
+ {
+ return $this->toString();
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see \IPLib\Range\RangeInterface::getAddressType()
+ */
+ public function getAddressType()
+ {
+ return $this->fromAddress->getAddressType();
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see \IPLib\Range\RangeInterface::getRangeType()
+ */
+ public function getRangeType()
+ {
+ if ($this->rangeType === null) {
+ $addressType = $this->getAddressType();
+ if ($addressType === AddressType::T_IPv6 && static::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;
+ }
+
+ /**
+ * {@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();
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Get the pattern 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;
+ }
+ }
+
+ /**
+ * {@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);
+ }
+}
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 0000000000..503178bb77
--- /dev/null
+++ b/vendor/mlocati/ip-lib/src/Range/Type.php
@@ -0,0 +1,141 @@
+<?php
+
+namespace IPLib\Range;
+
+/**
+ * Types of IP address classes.
+ */
+class Type
+{
+ /**
+ * Unspecified/unknown address.
+ *
+ * @var int
+ */
+ const T_UNSPECIFIED = 1;
+
+ /**
+ * Reserved/internal use only.
+ *
+ * @var int
+ */
+ const T_RESERVED = 2;
+
+ /**
+ * Refer to source hosts on "this" network.
+ *
+ * @var int
+ */
+ const T_THISNETWORK = 3;
+
+ /**
+ * Internet host loopback address.
+ *
+ * @var int
+ */
+ const T_LOOPBACK = 4;
+
+ /**
+ * Relay anycast address.
+ *
+ * @var int
+ */
+ const T_ANYCASTRELAY = 5;
+
+ /**
+ * "Limited broadcast" destination address.
+ *
+ * @var int
+ */
+ const T_LIMITEDBROADCAST = 6;
+
+ /**
+ * Multicast address assignments - Indentify a group of interfaces.
+ *
+ * @var int
+ */
+ const T_MULTICAST = 7;
+
+ /**
+ * "Link local" address, allocated for communication between hosts on a single link.
+ *
+ * @var int
+ */
+ const T_LINKLOCAL = 8;
+
+ /**
+ * Link local unicast / Linked-scoped unicast.
+ *
+ * @var int
+ */
+ const T_LINKLOCAL_UNICAST = 9;
+
+ /**
+ * Discard-Only address.
+ *
+ * @var int
+ */
+ const T_DISCARDONLY = 10;
+
+ /**
+ * Discard address.
+ *
+ * @var int
+ */
+ const T_DISCARD = 11;
+
+ /**
+ * For use in private networks.
+ *
+ * @var int
+ */
+ const T_PRIVATENETWORK = 12;
+
+ /**
+ * Public address.
+ *
+ * @var int
+ */
+ const T_PUBLIC = 13;
+
+ /**
+ * Get the name of a type.
+ *
+ * @param int $type
+ *
+ * @return string
+ */
+ public static function getName($type)
+ {
+ switch ($type) {
+ case static::T_UNSPECIFIED:
+ return 'Unspecified/unknown address';
+ case static::T_RESERVED:
+ return 'Reserved/internal use only';
+ case static::T_THISNETWORK:
+ return 'Refer to source hosts on "this" network';
+ case static::T_LOOPBACK:
+ return 'Internet host loopback address';
+ case static::T_ANYCASTRELAY:
+ return 'Relay anycast address';
+ case static::T_LIMITEDBROADCAST:
+ return '"Limited broadcast" destination address';
+ case static::T_MULTICAST:
+ 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';
+ case static::T_LINKLOCAL_UNICAST:
+ return 'Link local unicast / Linked-scoped unicast';
+ case static::T_DISCARDONLY:
+ return 'Discard only';
+ case static::T_DISCARD:
+ return 'Discard';
+ case static::T_PRIVATENETWORK:
+ return 'For use in private networks';
+ case static::T_PUBLIC:
+ return 'Public address';
+ default:
+ return $type === null ? 'Unknown type' : sprintf('Unknown type (%s)', $type);
+ }
+ }
+}