Преглед изворни кода

imported mlocati/ip-lib version 1.14.0

El RIDO пре 5 година
родитељ
комит
89f6f0051d

+ 2 - 2
composer.json

@@ -27,7 +27,7 @@
 		"php" : "^5.6.0 || ^7.0 || ^8.0",
 		"paragonie/random_compat" : "2.0.19",
 		"yzalis/identicon" : "2.0.0",
-		"mlocati/ip-lib": "^1.14"
+		"mlocati/ip-lib" : "1.14.0"
 	},
 	"require-dev" : {
 		"phpunit/phpunit" : "^4.6 || ^5.0"
@@ -40,4 +40,4 @@
 	"config" : {
 		"autoloader-suffix" : "DontChange"
 	}
-}
+}

+ 6 - 6
composer.lock

@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "8138b0e4bb3fcaab9ca4c02bbe35d891",
+    "content-hash": "c3aa7487ba976536cd3db9a60473ce67",
     "packages": [
         {
             "name": "mlocati/ip-lib",
@@ -1495,16 +1495,16 @@
         },
         {
             "name": "symfony/yaml",
-            "version": "v4.4.21",
+            "version": "v4.4.24",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/yaml.git",
-                "reference": "3871c720871029f008928244e56cf43497da7e9d"
+                "reference": "8b6d1b97521e2f125039b3fcb4747584c6dfa0ef"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/yaml/zipball/3871c720871029f008928244e56cf43497da7e9d",
-                "reference": "3871c720871029f008928244e56cf43497da7e9d",
+                "url": "https://api.github.com/repos/symfony/yaml/zipball/8b6d1b97521e2f125039b3fcb4747584c6dfa0ef",
+                "reference": "8b6d1b97521e2f125039b3fcb4747584c6dfa0ef",
                 "shasum": ""
             },
             "require": {
@@ -1559,7 +1559,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-03-05T17:58:50+00:00"
+            "time": "2021-05-16T09:52:47+00:00"
         },
         {
             "name": "webmozart/assert",

+ 14 - 0
vendor/composer/autoload_classmap.php

@@ -6,6 +6,20 @@ $vendorDir = dirname(dirname(__FILE__));
 $baseDir = dirname($vendorDir);
 
 return array(
+    'IPLib\\Address\\AddressInterface' => $vendorDir . '/mlocati/ip-lib/src/Address/AddressInterface.php',
+    'IPLib\\Address\\AssignedRange' => $vendorDir . '/mlocati/ip-lib/src/Address/AssignedRange.php',
+    'IPLib\\Address\\IPv4' => $vendorDir . '/mlocati/ip-lib/src/Address/IPv4.php',
+    'IPLib\\Address\\IPv6' => $vendorDir . '/mlocati/ip-lib/src/Address/IPv6.php',
+    'IPLib\\Address\\Type' => $vendorDir . '/mlocati/ip-lib/src/Address/Type.php',
+    'IPLib\\Factory' => $vendorDir . '/mlocati/ip-lib/src/Factory.php',
+    'IPLib\\Range\\AbstractRange' => $vendorDir . '/mlocati/ip-lib/src/Range/AbstractRange.php',
+    'IPLib\\Range\\Pattern' => $vendorDir . '/mlocati/ip-lib/src/Range/Pattern.php',
+    'IPLib\\Range\\RangeInterface' => $vendorDir . '/mlocati/ip-lib/src/Range/RangeInterface.php',
+    'IPLib\\Range\\Single' => $vendorDir . '/mlocati/ip-lib/src/Range/Single.php',
+    'IPLib\\Range\\Subnet' => $vendorDir . '/mlocati/ip-lib/src/Range/Subnet.php',
+    'IPLib\\Range\\Type' => $vendorDir . '/mlocati/ip-lib/src/Range/Type.php',
+    'IPLib\\Service\\BinaryMath' => $vendorDir . '/mlocati/ip-lib/src/Service/BinaryMath.php',
+    'IPLib\\Service\\RangesFromBounradyCalculator' => $vendorDir . '/mlocati/ip-lib/src/Service/RangesFromBounradyCalculator.php',
     'Identicon\\Generator\\BaseGenerator' => $vendorDir . '/yzalis/identicon/src/Identicon/Generator/BaseGenerator.php',
     'Identicon\\Generator\\GdGenerator' => $vendorDir . '/yzalis/identicon/src/Identicon/Generator/GdGenerator.php',
     'Identicon\\Generator\\GeneratorInterface' => $vendorDir . '/yzalis/identicon/src/Identicon/Generator/GeneratorInterface.php',

+ 1 - 0
vendor/composer/autoload_psr4.php

@@ -8,4 +8,5 @@ $baseDir = dirname($vendorDir);
 return array(
     'PrivateBin\\' => array($baseDir . '/lib'),
     'Identicon\\' => array($vendorDir . '/yzalis/identicon/src/Identicon'),
+    'IPLib\\' => array($vendorDir . '/mlocati/ip-lib/src'),
 );

+ 19 - 0
vendor/composer/autoload_static.php

@@ -18,6 +18,7 @@ class ComposerStaticInitDontChange
         'I' => 
         array (
             'Identicon\\' => 10,
+            'IPLib\\' => 6,
         ),
     );
 
@@ -30,9 +31,27 @@ class ComposerStaticInitDontChange
         array (
             0 => __DIR__ . '/..' . '/yzalis/identicon/src/Identicon',
         ),
+        'IPLib\\' => 
+        array (
+            0 => __DIR__ . '/..' . '/mlocati/ip-lib/src',
+        ),
     );
 
     public static $classMap = array (
+        'IPLib\\Address\\AddressInterface' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Address/AddressInterface.php',
+        'IPLib\\Address\\AssignedRange' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Address/AssignedRange.php',
+        'IPLib\\Address\\IPv4' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Address/IPv4.php',
+        'IPLib\\Address\\IPv6' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Address/IPv6.php',
+        'IPLib\\Address\\Type' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Address/Type.php',
+        'IPLib\\Factory' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Factory.php',
+        'IPLib\\Range\\AbstractRange' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Range/AbstractRange.php',
+        'IPLib\\Range\\Pattern' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Range/Pattern.php',
+        'IPLib\\Range\\RangeInterface' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Range/RangeInterface.php',
+        'IPLib\\Range\\Single' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Range/Single.php',
+        'IPLib\\Range\\Subnet' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Range/Subnet.php',
+        'IPLib\\Range\\Type' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Range/Type.php',
+        'IPLib\\Service\\BinaryMath' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Service/BinaryMath.php',
+        'IPLib\\Service\\RangesFromBounradyCalculator' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Service/RangesFromBounradyCalculator.php',
         'Identicon\\Generator\\BaseGenerator' => __DIR__ . '/..' . '/yzalis/identicon/src/Identicon/Generator/BaseGenerator.php',
         'Identicon\\Generator\\GdGenerator' => __DIR__ . '/..' . '/yzalis/identicon/src/Identicon/Generator/GdGenerator.php',
         'Identicon\\Generator\\GeneratorInterface' => __DIR__ . '/..' . '/yzalis/identicon/src/Identicon/Generator/GeneratorInterface.php',

+ 13 - 0
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;
+        }
+    }
+);

+ 125 - 0
vendor/mlocati/ip-lib/src/Address/AddressInterface.php

@@ -0,0 +1,125 @@
+<?php
+
+namespace IPLib\Address;
+
+use IPLib\Range\RangeInterface;
+
+/**
+ * Interface of all the IP address types.
+ */
+interface AddressInterface
+{
+    /**
+     * Get the short string representation of this address.
+     *
+     * @return string
+     */
+    public function __toString();
+
+    /**
+     * Get the number of bits representing this address type.
+     *
+     * @return int
+     *
+     * @example 32 for IPv4
+     * @example 128 for IPv6
+     */
+    public static function getNumberOfBits();
+
+    /**
+     * 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 byte list of the IP address.
+     *
+     * @return int[]
+     *
+     * @example For localhost: 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 full bit list the IP address.
+     *
+     * @return string
+     *
+     * @example For localhost: For IPv4 you'll get '01111111000000000000000000000001' (32 digits), for IPv6 '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001' (128 digits)
+     */
+    public function getBits();
+
+    /**
+     * 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();
+
+    /**
+     * Get the Reverse DNS Lookup Address of this IP address.
+     *
+     * @return string
+     *
+     * @example for IPv4 it returns something like x.x.x.x.in-addr.arpa
+     * @example for IPv6 it returns something like x.x.x.x..x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.ip6.arpa
+     */
+    public function getReverseDNSLookupName();
+}

+ 138 - 0
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|false|null return NULL of the range is fully outside this range; false if it's partly crosses this range (or it contains mixed types); a \IPLib\Range\Type::T_ constant otherwise
+     */
+    public function getRangeType(RangeInterface $range)
+    {
+        $myStart = $this->range->getComparableStartString();
+        $rangeEnd = $range->getComparableEndString();
+        if ($myStart > $rangeEnd) {
+            $result = null;
+        } else {
+            $myEnd = $this->range->getComparableEndString();
+            $rangeStart = $range->getComparableStartString();
+            if ($myEnd < $rangeStart) {
+                $result = null;
+            } elseif ($rangeStart < $myStart || $rangeEnd > $myEnd) {
+                $result = false;
+            } else {
+                $result = null;
+                foreach ($this->exceptions as $exception) {
+                    $result = $exception->getRangeType($range);
+                    if ($result !== null) {
+                        break;
+                    }
+                }
+                if ($result === null) {
+                    $result = $this->getType();
+                }
+            }
+        }
+
+        return $result;
+    }
+}

+ 465 - 0
vendor/mlocati/ip-lib/src/Address/IPv4.php

@@ -0,0 +1,465 @@
+<?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;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::__toString()
+     */
+    public function __toString()
+    {
+        return $this->address;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::getNumberOfBits()
+     */
+    public static function getNumberOfBits()
+    {
+        return 32;
+    }
+
+    /**
+     * Parse a string and returns an IPv4 instance if the string is valid, or null otherwise.
+     *
+     * @param string|mixed $address the address to parse
+     * @param bool $mayIncludePort set to false to avoid parsing addresses with ports
+     * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
+     *
+     * @return static|null
+     */
+    public static function fromString($address, $mayIncludePort = true, $supportNonDecimalIPv4 = false)
+    {
+        if (!is_string($address) || !strpos($address, '.')) {
+            return null;
+        }
+        $rxChunk = '0?[0-9]{1,3}';
+        if ($supportNonDecimalIPv4) {
+            $rxChunk = "(?:0[Xx]0*[0-9A-Fa-f]{1,2})|(?:{$rxChunk})";
+        }
+        $rx = "0*?({$rxChunk})\.0*?({$rxChunk})\.0*?({$rxChunk})\.0*?({$rxChunk})";
+        if ($mayIncludePort) {
+            $rx .= '(?::\d+)?';
+        }
+        $matches = null;
+        if (!preg_match('/^' . $rx . '$/', $address, $matches)) {
+            return null;
+        }
+        $nums = array();
+        for ($i = 1; $i <= 4; $i++) {
+            $s = $matches[$i];
+            if ($supportNonDecimalIPv4) {
+                if (stripos($s, '0x') === 0) {
+                    $n = hexdec(substr($s, 2));
+                } elseif ($s[0] === '0') {
+                    if (!preg_match('/^[0-7]+$/', $s)) {
+                        return null;
+                    }
+                    $n = octdec(substr($s, 1));
+                } else {
+                    $n = (int) $s;
+                }
+            } else {
+                $n = (int) $s;
+            }
+            if ($n < 0 || $n > 255) {
+                return null;
+            }
+            $nums[] = (string) $n;
+        }
+
+        return new static(implode('.', $nums));
+    }
+
+    /**
+     * Parse an array of bytes and returns an IPv4 instance if the array is valid, or null otherwise.
+     *
+     * @param int[]|array $bytes
+     *
+     * @return static|null
+     */
+    public static function fromBytes(array $bytes)
+    {
+        $result = null;
+        if (count($bytes) === 4) {
+            $chunks = array_map(
+                function ($byte) {
+                    return (is_int($byte) && $byte >= 0 && $byte <= 255) ? (string) $byte : false;
+                },
+                $bytes
+            );
+            if (in_array(false, $chunks, true) === false) {
+                $result = new static(implode('.', $chunks));
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::toString()
+     */
+    public function toString($long = false)
+    {
+        if ($long) {
+            return $this->getComparableString();
+        }
+
+        return $this->address;
+    }
+
+    /**
+     * Get the octal representation of this IP address.
+     *
+     * @param bool $long
+     *
+     * @return string
+     *
+     * @example if $long == false: if the decimal representation is '0.7.8.255': '0.7.010.0377'
+     * @example if $long == true: if the decimal representation is '0.7.8.255': '0000.0007.0010.0377'
+     */
+    public function toOctal($long = false)
+    {
+        $chunks = array();
+        foreach ($this->getBytes() as $byte) {
+            if ($long) {
+                $chunks[] = sprintf('%04o', $byte);
+            } else {
+                $chunks[] = '0' . decoct($byte);
+            }
+        }
+
+        return implode('.', $chunks);
+    }
+
+    /**
+     * Get the hexadecimal representation of this IP address.
+     *
+     * @param bool $long
+     *
+     * @return string
+     *
+     * @example if $long == false: if the decimal representation is '0.9.10.255': '0.9.0xa.0xff'
+     * @example if $long == true: if the decimal representation is '0.9.10.255': '0x00.0x09.0x0a.0xff'
+     */
+    public function toHexadecimal($long = false)
+    {
+        $chunks = array();
+        foreach ($this->getBytes() as $byte) {
+            if ($long) {
+                $chunks[] = sprintf('0x%02x', $byte);
+            } else {
+                $chunks[] = '0x' . dechex($byte);
+            }
+        }
+
+        return implode('.', $chunks);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::getBytes()
+     */
+    public function getBytes()
+    {
+        if ($this->bytes === null) {
+            $this->bytes = array_map(
+                function ($chunk) {
+                    return (int) $chunk;
+                },
+                explode('.', $this->address)
+            );
+        }
+
+        return $this->bytes;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::getBits()
+     */
+    public function getBits()
+    {
+        $parts = array();
+        foreach ($this->getBytes() as $byte) {
+            $parts[] = sprintf('%08b', $byte);
+        }
+
+        return implode('', $parts);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::getAddressType()
+     */
+    public function getAddressType()
+    {
+        return Type::T_IPv4;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::getDefaultReservedRangeType()
+     */
+    public static function getDefaultReservedRangeType()
+    {
+        return RangeType::T_PUBLIC;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::getReservedRanges()
+     */
+    public static function getReservedRanges()
+    {
+        if (self::$reservedRanges === null) {
+            $reservedRanges = array();
+            foreach (array(
+                // RFC 5735
+                '0.0.0.0/8' => array(RangeType::T_THISNETWORK, array('0.0.0.0/32' => RangeType::T_UNSPECIFIED)),
+                // RFC 5735
+                '10.0.0.0/8' => array(RangeType::T_PRIVATENETWORK),
+                // RFC 6598
+                '100.64.0.0/10' => array(RangeType::T_CGNAT),
+                // RFC 5735
+                '127.0.0.0/8' => array(RangeType::T_LOOPBACK),
+                // RFC 5735
+                '169.254.0.0/16' => array(RangeType::T_LINKLOCAL),
+                // RFC 5735
+                '172.16.0.0/12' => array(RangeType::T_PRIVATENETWORK),
+                // RFC 5735
+                '192.0.0.0/24' => array(RangeType::T_RESERVED),
+                // RFC 5735
+                '192.0.2.0/24' => array(RangeType::T_RESERVED),
+                // RFC 5735
+                '192.88.99.0/24' => array(RangeType::T_ANYCASTRELAY),
+                // RFC 5735
+                '192.168.0.0/16' => array(RangeType::T_PRIVATENETWORK),
+                // RFC 5735
+                '198.18.0.0/15' => array(RangeType::T_RESERVED),
+                // RFC 5735
+                '198.51.100.0/24' => array(RangeType::T_RESERVED),
+                // RFC 5735
+                '203.0.113.0/24' => array(RangeType::T_RESERVED),
+                // RFC 5735
+                '224.0.0.0/4' => array(RangeType::T_MULTICAST),
+                // RFC 5735
+                '240.0.0.0/4' => array(RangeType::T_RESERVED, array('255.255.255.255/32' => RangeType::T_LIMITEDBROADCAST)),
+            ) as $range => $data) {
+                $exceptions = array();
+                if (isset($data[1])) {
+                    foreach ($data[1] as $exceptionRange => $exceptionType) {
+                        $exceptions[] = new AssignedRange(Subnet::fromString($exceptionRange), $exceptionType);
+                    }
+                }
+                $reservedRanges[] = new AssignedRange(Subnet::fromString($range), $data[0], $exceptions);
+            }
+            self::$reservedRanges = $reservedRanges;
+        }
+
+        return self::$reservedRanges;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::getRangeType()
+     */
+    public function getRangeType()
+    {
+        if ($this->rangeType === null) {
+            $rangeType = null;
+            foreach (static::getReservedRanges() as $reservedRange) {
+                $rangeType = $reservedRange->getAddressType($this);
+                if ($rangeType !== null) {
+                    break;
+                }
+            }
+            $this->rangeType = $rangeType === null ? static::getDefaultReservedRangeType() : $rangeType;
+        }
+
+        return $this->rangeType;
+    }
+
+    /**
+     * Create an IPv6 representation of this address (in 6to4 notation).
+     *
+     * @return \IPLib\Address\IPv6
+     */
+    public function toIPv6()
+    {
+        $myBytes = $this->getBytes();
+
+        return IPv6::fromString('2002:' . sprintf('%02x', $myBytes[0]) . sprintf('%02x', $myBytes[1]) . ':' . sprintf('%02x', $myBytes[2]) . sprintf('%02x', $myBytes[3]) . '::');
+    }
+
+    /**
+     * Create an IPv6 representation of this address (in IPv6 IPv4-mapped notation).
+     *
+     * @return \IPLib\Address\IPv6
+     */
+    public function toIPv6IPv4Mapped()
+    {
+        return IPv6::fromBytes(array_merge(array(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff), $this->getBytes()));
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::getComparableString()
+     */
+    public function getComparableString()
+    {
+        if ($this->comparableString === null) {
+            $chunks = array();
+            foreach ($this->getBytes() as $byte) {
+                $chunks[] = sprintf('%03d', $byte);
+            }
+            $this->comparableString = implode('.', $chunks);
+        }
+
+        return $this->comparableString;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::matches()
+     */
+    public function matches(RangeInterface $range)
+    {
+        return $range->contains($this);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::getNextAddress()
+     */
+    public function getNextAddress()
+    {
+        $overflow = false;
+        $bytes = $this->getBytes();
+        for ($i = count($bytes) - 1; $i >= 0; $i--) {
+            if ($bytes[$i] === 255) {
+                if ($i === 0) {
+                    $overflow = true;
+                    break;
+                }
+                $bytes[$i] = 0;
+            } else {
+                $bytes[$i]++;
+                break;
+            }
+        }
+
+        return $overflow ? null : static::fromBytes($bytes);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::getPreviousAddress()
+     */
+    public function getPreviousAddress()
+    {
+        $overflow = false;
+        $bytes = $this->getBytes();
+        for ($i = count($bytes) - 1; $i >= 0; $i--) {
+            if ($bytes[$i] === 0) {
+                if ($i === 0) {
+                    $overflow = true;
+                    break;
+                }
+                $bytes[$i] = 255;
+            } else {
+                $bytes[$i]--;
+                break;
+            }
+        }
+
+        return $overflow ? null : static::fromBytes($bytes);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::getReverseDNSLookupName()
+     */
+    public function getReverseDNSLookupName()
+    {
+        return implode(
+            '.',
+            array_reverse($this->getBytes())
+        ) . '.in-addr.arpa';
+    }
+}

+ 568 - 0
vendor/mlocati/ip-lib/src/Address/IPv6.php

@@ -0,0 +1,568 @@
+<?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;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::__toString()
+     */
+    public function __toString()
+    {
+        return $this->toString();
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::getNumberOfBits()
+     */
+    public static function getNumberOfBits()
+    {
+        return 128;
+    }
+
+    /**
+     * Parse a string and returns an IPv6 instance if the string is valid, or null otherwise.
+     *
+     * @param string|mixed $address the address to parse
+     * @param bool $mayIncludePort set to false to avoid parsing addresses with ports
+     * @param bool $mayIncludeZoneID set to false to avoid parsing addresses with zone IDs (see RFC 4007)
+     *
+     * @return static|null
+     */
+    public static function fromString($address, $mayIncludePort = true, $mayIncludeZoneID = true)
+    {
+        $result = null;
+        if (is_string($address) && strpos($address, ':') !== false && strpos($address, ':::') === false) {
+            $matches = null;
+            if ($mayIncludePort && $address[0] === '[' && preg_match('/^\[(.+)]:\d+$/', $address, $matches)) {
+                $address = $matches[1];
+            }
+            if ($mayIncludeZoneID) {
+                $percentagePos = strpos($address, '%');
+                if ($percentagePos > 0) {
+                    $address = substr($address, 0, $percentagePos);
+                }
+            }
+            if (preg_match('/^((?:[0-9a-f]*:+)+)(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/i', $address, $matches)) {
+                $address6 = static::fromString($matches[1] . '0:0', false);
+                if ($address6 !== null) {
+                    $address4 = IPv4::fromString($matches[2], false);
+                    if ($address4 !== null) {
+                        $bytes4 = $address4->getBytes();
+                        $address6->longAddress = substr($address6->longAddress, 0, -9) . sprintf('%02x%02x:%02x%02x', $bytes4[0], $bytes4[1], $bytes4[2], $bytes4[3]);
+                        $result = $address6;
+                    }
+                }
+            } else {
+                if (strpos($address, '::') === false) {
+                    $chunks = explode(':', $address);
+                } else {
+                    $chunks = array();
+                    $parts = explode('::', $address);
+                    if (count($parts) === 2) {
+                        $before = ($parts[0] === '') ? array() : explode(':', $parts[0]);
+                        $after = ($parts[1] === '') ? array() : explode(':', $parts[1]);
+                        $missing = 8 - count($before) - count($after);
+                        if ($missing >= 0) {
+                            $chunks = $before;
+                            if ($missing !== 0) {
+                                $chunks = array_merge($chunks, array_fill(0, $missing, '0'));
+                            }
+                            $chunks = array_merge($chunks, $after);
+                        }
+                    }
+                }
+                if (count($chunks) === 8) {
+                    $nums = array_map(
+                        function ($chunk) {
+                            return preg_match('/^[0-9A-Fa-f]{1,4}$/', $chunk) ? hexdec($chunk) : false;
+                        },
+                        $chunks
+                    );
+                    if (!in_array(false, $nums, true)) {
+                        $longAddress = implode(
+                            ':',
+                            array_map(
+                                function ($num) {
+                                    return sprintf('%04x', $num);
+                                },
+                                $nums
+                            )
+                        );
+                        $result = new static($longAddress);
+                    }
+                }
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Parse an array of bytes and returns an IPv6 instance if the array is valid, or null otherwise.
+     *
+     * @param int[]|array $bytes
+     *
+     * @return static|null
+     */
+    public static function fromBytes(array $bytes)
+    {
+        $result = null;
+        if (count($bytes) === 16) {
+            $address = '';
+            for ($i = 0; $i < 16; $i++) {
+                if ($i !== 0 && $i % 2 === 0) {
+                    $address .= ':';
+                }
+                $byte = $bytes[$i];
+                if (is_int($byte) && $byte >= 0 && $byte <= 255) {
+                    $address .= sprintf('%02x', $byte);
+                } else {
+                    $address = null;
+                    break;
+                }
+            }
+            if ($address !== null) {
+                $result = new static($address);
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Parse an array of words and returns an IPv6 instance if the array is valid, or null otherwise.
+     *
+     * @param int[]|array $words
+     *
+     * @return static|null
+     */
+    public static function fromWords(array $words)
+    {
+        $result = null;
+        if (count($words) === 8) {
+            $chunks = array();
+            for ($i = 0; $i < 8; $i++) {
+                $word = $words[$i];
+                if (is_int($word) && $word >= 0 && $word <= 0xffff) {
+                    $chunks[] = sprintf('%04x', $word);
+                } else {
+                    $chunks = null;
+                    break;
+                }
+            }
+            if ($chunks !== null) {
+                $result = new static(implode(':', $chunks));
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::toString()
+     */
+    public function toString($long = false)
+    {
+        if ($long) {
+            $result = $this->longAddress;
+        } else {
+            if ($this->shortAddress === null) {
+                if (strpos($this->longAddress, '0000:0000:0000:0000:0000:ffff:') === 0) {
+                    $lastBytes = array_slice($this->getBytes(), -4);
+                    $this->shortAddress = '::ffff:' . implode('.', $lastBytes);
+                } else {
+                    $chunks = array_map(
+                        function ($word) {
+                            return dechex($word);
+                        },
+                        $this->getWords()
+                    );
+                    $shortAddress = implode(':', $chunks);
+                    $matches = null;
+                    for ($i = 8; $i > 1; $i--) {
+                        $search = '(?:^|:)' . rtrim(str_repeat('0:', $i), ':') . '(?:$|:)';
+                        if (preg_match('/^(.*?)' . $search . '(.*)$/', $shortAddress, $matches)) {
+                            $shortAddress = $matches[1] . '::' . $matches[2];
+                            break;
+                        }
+                    }
+                    $this->shortAddress = $shortAddress;
+                }
+            }
+            $result = $this->shortAddress;
+        }
+
+        return $result;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::getBytes()
+     */
+    public function getBytes()
+    {
+        if ($this->bytes === null) {
+            $bytes = array();
+            foreach ($this->getWords() as $word) {
+                $bytes[] = $word >> 8;
+                $bytes[] = $word & 0xff;
+            }
+            $this->bytes = $bytes;
+        }
+
+        return $this->bytes;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::getBits()
+     */
+    public function getBits()
+    {
+        $parts = array();
+        foreach ($this->getBytes() as $byte) {
+            $parts[] = sprintf('%08b', $byte);
+        }
+
+        return implode('', $parts);
+    }
+
+    /**
+     * Get the word list of the IP address.
+     *
+     * @return int[]
+     */
+    public function getWords()
+    {
+        if ($this->words === null) {
+            $this->words = array_map(
+                function ($chunk) {
+                    return hexdec($chunk);
+                },
+                explode(':', $this->longAddress)
+            );
+        }
+
+        return $this->words;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::getAddressType()
+     */
+    public function getAddressType()
+    {
+        return Type::T_IPv6;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::getDefaultReservedRangeType()
+     */
+    public static function getDefaultReservedRangeType()
+    {
+        return RangeType::T_RESERVED;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::getReservedRanges()
+     */
+    public static function getReservedRanges()
+    {
+        if (self::$reservedRanges === null) {
+            $reservedRanges = array();
+            foreach (array(
+                // RFC 4291
+                '::/128' => array(RangeType::T_UNSPECIFIED),
+                // RFC 4291
+                '::1/128' => array(RangeType::T_LOOPBACK),
+                // RFC 4291
+                '100::/8' => array(RangeType::T_DISCARD, array('100::/64' => RangeType::T_DISCARDONLY)),
+                //'2002::/16' => array(RangeType::),
+                // RFC 4291
+                '2000::/3' => array(RangeType::T_PUBLIC),
+                // RFC 4193
+                'fc00::/7' => array(RangeType::T_PRIVATENETWORK),
+                // RFC 4291
+                'fe80::/10' => array(RangeType::T_LINKLOCAL_UNICAST),
+                // RFC 4291
+                'ff00::/8' => array(RangeType::T_MULTICAST),
+                // RFC 4291
+                //'::/8' => array(RangeType::T_RESERVED),
+                // RFC 4048
+                //'200::/7' => array(RangeType::T_RESERVED),
+                // RFC 4291
+                //'400::/6' => array(RangeType::T_RESERVED),
+                // RFC 4291
+                //'800::/5' => array(RangeType::T_RESERVED),
+                // RFC 4291
+                //'1000::/4' => array(RangeType::T_RESERVED),
+                // RFC 4291
+                //'4000::/3' => array(RangeType::T_RESERVED),
+                // RFC 4291
+                //'6000::/3' => array(RangeType::T_RESERVED),
+                // RFC 4291
+                //'8000::/3' => array(RangeType::T_RESERVED),
+                // RFC 4291
+                //'a000::/3' => array(RangeType::T_RESERVED),
+                // RFC 4291
+                //'c000::/3' => array(RangeType::T_RESERVED),
+                // RFC 4291
+                //'e000::/4' => array(RangeType::T_RESERVED),
+                // RFC 4291
+                //'f000::/5' => array(RangeType::T_RESERVED),
+                // RFC 4291
+                //'f800::/6' => array(RangeType::T_RESERVED),
+                // RFC 4291
+                //'fe00::/9' => array(RangeType::T_RESERVED),
+                // RFC 3879
+                //'fec0::/10' => array(RangeType::T_RESERVED),
+            ) as $range => $data) {
+                $exceptions = array();
+                if (isset($data[1])) {
+                    foreach ($data[1] as $exceptionRange => $exceptionType) {
+                        $exceptions[] = new AssignedRange(Subnet::fromString($exceptionRange), $exceptionType);
+                    }
+                }
+                $reservedRanges[] = new AssignedRange(Subnet::fromString($range), $data[0], $exceptions);
+            }
+            self::$reservedRanges = $reservedRanges;
+        }
+
+        return self::$reservedRanges;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::getRangeType()
+     */
+    public function getRangeType()
+    {
+        if ($this->rangeType === null) {
+            $ipv4 = $this->toIPv4();
+            if ($ipv4 !== null) {
+                $this->rangeType = $ipv4->getRangeType();
+            } else {
+                $rangeType = null;
+                foreach (static::getReservedRanges() as $reservedRange) {
+                    $rangeType = $reservedRange->getAddressType($this);
+                    if ($rangeType !== null) {
+                        break;
+                    }
+                }
+                $this->rangeType = $rangeType === null ? static::getDefaultReservedRangeType() : $rangeType;
+            }
+        }
+
+        return $this->rangeType;
+    }
+
+    /**
+     * Create an IPv4 representation of this address (if possible, otherwise returns null).
+     *
+     * @return \IPLib\Address\IPv4|null
+     */
+    public function toIPv4()
+    {
+        if (strpos($this->longAddress, '2002:') === 0) {
+            // 6to4
+            return IPv4::fromBytes(array_slice($this->getBytes(), 2, 4));
+        }
+        if (strpos($this->longAddress, '0000:0000:0000:0000:0000:ffff:') === 0) {
+            // IPv4-mapped IPv6 addresses
+            return IPv4::fromBytes(array_slice($this->getBytes(), -4));
+        }
+
+        return null;
+    }
+
+    /**
+     * Render this IPv6 address in the "mixed" IPv6 (first 12 bytes) + IPv4 (last 4 bytes) mixed syntax.
+     *
+     * @param bool $ipV6Long render the IPv6 part in "long" format?
+     * @param bool $ipV4Long render the IPv4 part in "long" format?
+     *
+     * @return string
+     *
+     * @example '::13.1.68.3'
+     * @example '0000:0000:0000:0000:0000:0000:13.1.68.3' when $ipV6Long is true
+     * @example '::013.001.068.003' when $ipV4Long is true
+     * @example '0000:0000:0000:0000:0000:0000:013.001.068.003' when $ipV6Long and $ipV4Long are true
+     *
+     * @see https://tools.ietf.org/html/rfc4291#section-2.2 point 3.
+     */
+    public function toMixedIPv6IPv4String($ipV6Long = false, $ipV4Long = false)
+    {
+        $myBytes = $this->getBytes();
+        $ipv6Bytes = array_merge(array_slice($myBytes, 0, 12), array(0xff, 0xff, 0xff, 0xff));
+        $ipv6String = static::fromBytes($ipv6Bytes)->toString($ipV6Long);
+        $ipv4Bytes = array_slice($myBytes, 12, 4);
+        $ipv4String = IPv4::fromBytes($ipv4Bytes)->toString($ipV4Long);
+
+        return preg_replace('/((ffff:ffff)|(\d+(\.\d+){3}))$/i', $ipv4String, $ipv6String);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::getComparableString()
+     */
+    public function getComparableString()
+    {
+        return $this->longAddress;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::matches()
+     */
+    public function matches(RangeInterface $range)
+    {
+        return $range->contains($this);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::getNextAddress()
+     */
+    public function getNextAddress()
+    {
+        $overflow = false;
+        $words = $this->getWords();
+        for ($i = count($words) - 1; $i >= 0; $i--) {
+            if ($words[$i] === 0xffff) {
+                if ($i === 0) {
+                    $overflow = true;
+                    break;
+                }
+                $words[$i] = 0;
+            } else {
+                $words[$i]++;
+                break;
+            }
+        }
+
+        return $overflow ? null : static::fromWords($words);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::getPreviousAddress()
+     */
+    public function getPreviousAddress()
+    {
+        $overflow = false;
+        $words = $this->getWords();
+        for ($i = count($words) - 1; $i >= 0; $i--) {
+            if ($words[$i] === 0) {
+                if ($i === 0) {
+                    $overflow = true;
+                    break;
+                }
+                $words[$i] = 0xffff;
+            } else {
+                $words[$i]--;
+                break;
+            }
+        }
+
+        return $overflow ? null : static::fromWords($words);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Address\AddressInterface::getReverseDNSLookupName()
+     */
+    public function getReverseDNSLookupName()
+    {
+        return implode(
+            '.',
+            array_reverse(str_split(str_replace(':', '', $this->toString(true)), 1))
+        ) . '.ip6.arpa';
+    }
+}

+ 42 - 0
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);
+        }
+    }
+}

+ 205 - 0
vendor/mlocati/ip-lib/src/Factory.php

@@ -0,0 +1,205 @@
+<?php
+
+namespace IPLib;
+
+use IPLib\Address\AddressInterface;
+use IPLib\Range\Subnet;
+use IPLib\Service\RangesFromBounradyCalculator;
+
+/**
+ * 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)
+     * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
+     *
+     * @return \IPLib\Address\AddressInterface|null
+     */
+    public static function addressFromString($address, $mayIncludePort = true, $mayIncludeZoneID = true, $supportNonDecimalIPv4 = false)
+    {
+        $result = null;
+        if ($result === null) {
+            $result = Address\IPv4::fromString($address, $mayIncludePort, $supportNonDecimalIPv4);
+        }
+        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
+     * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
+     *
+     * @return \IPLib\Range\RangeInterface|null
+     */
+    public static function rangeFromString($range, $supportNonDecimalIPv4 = false)
+    {
+        $result = null;
+        if ($result === null) {
+            $result = Range\Subnet::fromString($range, $supportNonDecimalIPv4);
+        }
+        if ($result === null) {
+            $result = Range\Pattern::fromString($range, $supportNonDecimalIPv4);
+        }
+        if ($result === null) {
+            $result = Range\Single::fromString($range, $supportNonDecimalIPv4);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Create the smallest address range that comprises two addresses.
+     *
+     * @param string|\IPLib\Address\AddressInterface $from
+     * @param string|\IPLib\Address\AddressInterface $to
+     * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
+     *
+     * @return \IPLib\Range\RangeInterface|null return NULL if $from and/or $to are invalid addresses, or if both are NULL or empty strings, or if they are addresses of different types
+     */
+    public static function rangeFromBoundaries($from, $to, $supportNonDecimalIPv4 = false)
+    {
+        list($from, $to) = self::parseBoundaries($from, $to, $supportNonDecimalIPv4);
+
+        return $from === false || $to === false ? null : static::rangeFromBoundaryAddresses($from, $to);
+    }
+
+    /**
+     * Create a list of Range instances that exactly describes all the addresses between the two provided addresses.
+     *
+     * @param string|\IPLib\Address\AddressInterface $from
+     * @param string|\IPLib\Address\AddressInterface $to
+     * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
+     *
+     * @return \IPLib\Range\Subnet[]|null return NULL if $from and/or $to are invalid addresses, or if both are NULL or empty strings, or if they are addresses of different types
+     */
+    public static function rangesFromBoundaries($from, $to, $supportNonDecimalIPv4 = false)
+    {
+        list($from, $to) = self::parseBoundaries($from, $to, $supportNonDecimalIPv4);
+        if (($from === false || $to === false) || ($from === null && $to === null)) {
+            return null;
+        }
+        if ($from === null || $to === null) {
+            $address = $from ? $from : $to;
+
+            return array(new Subnet($address, $address, $address->getNumberOfBits()));
+        }
+        $numberOfBits = $from->getNumberOfBits();
+        if ($to->getNumberOfBits() !== $numberOfBits) {
+            return null;
+        }
+        $calculator = new RangesFromBounradyCalculator($numberOfBits);
+
+        return $calculator->getRanges($from, $to);
+    }
+
+    /**
+     * @param \IPLib\Address\AddressInterface $from
+     * @param \IPLib\Address\AddressInterface $to
+     *
+     * @return \IPLib\Range\RangeInterface|null
+     */
+    protected static function rangeFromBoundaryAddresses(AddressInterface $from = null, AddressInterface $to = null)
+    {
+        if ($from === null && $to === null) {
+            $result = null;
+        } elseif ($to === null) {
+            $result = Range\Single::fromAddress($from);
+        } elseif ($from === null) {
+            $result = Range\Single::fromAddress($to);
+        } else {
+            $result = null;
+            $addressType = $from->getAddressType();
+            if ($addressType === $to->getAddressType()) {
+                $cmp = strcmp($from->getComparableString(), $to->getComparableString());
+                if ($cmp === 0) {
+                    $result = Range\Single::fromAddress($from);
+                } else {
+                    if ($cmp > 0) {
+                        list($from, $to) = array($to, $from);
+                    }
+                    $fromBytes = $from->getBytes();
+                    $toBytes = $to->getBytes();
+                    $numBytes = count($fromBytes);
+                    $sameBits = 0;
+                    for ($byteIndex = 0; $byteIndex < $numBytes; $byteIndex++) {
+                        $fromByte = $fromBytes[$byteIndex];
+                        $toByte = $toBytes[$byteIndex];
+                        if ($fromByte === $toByte) {
+                            $sameBits += 8;
+                        } else {
+                            $differentBitsInByte = decbin($fromByte ^ $toByte);
+                            $sameBits += 8 - strlen($differentBitsInByte);
+                            break;
+                        }
+                    }
+                    $result = static::rangeFromString($from->toString(true) . '/' . (string) $sameBits);
+                }
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * @param string|\IPLib\Address\AddressInterface $from
+     * @param string|\IPLib\Address\AddressInterface $to
+     * @param bool $supportNonDecimalIPv4
+     *
+     * @return \IPLib\Address\AddressInterface[]|null[]|false[]
+     */
+    private static function parseBoundaries($from, $to, $supportNonDecimalIPv4 = false)
+    {
+        $result = array();
+        foreach (array('from', 'to') as $param) {
+            $value = $$param;
+            if (!($value instanceof AddressInterface)) {
+                $value = (string) $value;
+                if ($value === '') {
+                    $value = null;
+                } else {
+                    $value = static::addressFromString($value, true, true, $supportNonDecimalIPv4);
+                    if ($value === null) {
+                        $value = false;
+                    }
+                }
+            }
+            $result[] = $value;
+        }
+        if ($result[0] && $result[1] && strcmp($result[0]->getComparableString(), $result[1]->getComparableString()) > 0) {
+            $result = array($result[1], $result[0]);
+        }
+
+        return $result;
+    }
+}

+ 95 - 0
vendor/mlocati/ip-lib/src/Range/AbstractRange.php

@@ -0,0 +1,95 @@
+<?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;
+
+abstract class AbstractRange implements RangeInterface
+{
+    /**
+     * {@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;
+    }
+}

+ 275 - 0
vendor/mlocati/ip-lib/src/Range/Pattern.php

@@ -0,0 +1,275 @@
+<?php
+
+namespace IPLib\Range;
+
+use IPLib\Address\AddressInterface;
+use IPLib\Address\IPv4;
+use IPLib\Address\IPv6;
+use IPLib\Address\Type as AddressType;
+
+/**
+ * Represents an address range in pattern format (only ending asterisks are supported).
+ *
+ * @example 127.0.*.*
+ * @example ::/8
+ */
+class Pattern extends AbstractRange
+{
+    /**
+     * 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|false|null 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;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::__toString()
+     */
+    public function __toString()
+    {
+        return $this->toString();
+    }
+
+    /**
+     * Try get the range instance starting from its string representation.
+     *
+     * @param string|mixed $range
+     * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
+     *
+     * @return static|null
+     */
+    public static function fromString($range, $supportNonDecimalIPv4 = false)
+    {
+        if (!is_string($range) || strpos($range, '*') === false) {
+            return null;
+        }
+        if ($range === '*.*.*.*') {
+            return new static(IPv4::fromString('0.0.0.0'), IPv4::fromString('255.255.255.255'), 4);
+        }
+        if ($range === '*:*:*:*:*:*:*:*') {
+            return new static(IPv6::fromString('::'), IPv6::fromString('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'), 8);
+        }
+        $matches = null;
+        if (strpos($range, '.') !== false && preg_match('/^[^*]+((?:\.\*)+)$/', $range, $matches)) {
+            $asterisksCount = strlen($matches[1]) >> 1;
+            if ($asterisksCount > 0) {
+                $missingDots = 3 - substr_count($range, '.');
+                if ($missingDots > 0) {
+                    $range .= str_repeat('.*', $missingDots);
+                    $asterisksCount += $missingDots;
+                }
+            }
+            $fromAddress = IPv4::fromString(str_replace('*', '0', $range), true, $supportNonDecimalIPv4);
+            if ($fromAddress === null) {
+                return null;
+            }
+            $fixedBytes = array_slice($fromAddress->getBytes(), 0, -$asterisksCount);
+            $otherBytes = array_fill(0, $asterisksCount, 255);
+            $toAddress = IPv4::fromBytes(array_merge($fixedBytes, $otherBytes));
+
+            return new static($fromAddress, $toAddress, $asterisksCount);
+        }
+        if (strpos($range, ':') !== false && preg_match('/^[^*]+((?::\*)+)$/', $range, $matches)) {
+            $asterisksCount = strlen($matches[1]) >> 1;
+            $fromAddress = IPv6::fromString(str_replace('*', '0', $range));
+            if ($fromAddress === null) {
+                return null;
+            }
+            $fixedWords = array_slice($fromAddress->getWords(), 0, -$asterisksCount);
+            $otherWords = array_fill(0, $asterisksCount, 0xffff);
+            $toAddress = IPv6::fromWords(array_merge($fixedWords, $otherWords));
+
+            return new static($fromAddress, $toAddress, $asterisksCount);
+        }
+
+        return null;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::toString()
+     */
+    public function toString($long = false)
+    {
+        if ($this->asterisksCount === 0) {
+            return $this->fromAddress->toString($long);
+        }
+        switch (true) {
+            case $this->fromAddress instanceof \IPLib\Address\IPv4:
+                $chunks = explode('.', $this->fromAddress->toString());
+                $chunks = array_slice($chunks, 0, -$this->asterisksCount);
+                $chunks = array_pad($chunks, 4, '*');
+                $result = implode('.', $chunks);
+                break;
+            case $this->fromAddress instanceof \IPLib\Address\IPv6:
+                if ($long) {
+                    $chunks = explode(':', $this->fromAddress->toString(true));
+                    $chunks = array_slice($chunks, 0, -$this->asterisksCount);
+                    $chunks = array_pad($chunks, 8, '*');
+                    $result = implode(':', $chunks);
+                } elseif ($this->asterisksCount === 8) {
+                    $result = '*:*:*:*:*:*:*:*';
+                } else {
+                    $bytes = $this->toAddress->getBytes();
+                    $bytes = array_slice($bytes, 0, -$this->asterisksCount * 2);
+                    $bytes = array_pad($bytes, 16, 1);
+                    $address = IPv6::fromBytes($bytes);
+                    $before = substr($address->toString(false), 0, -strlen(':101') * $this->asterisksCount);
+                    $result = $before . str_repeat(':*', $this->asterisksCount);
+                }
+                break;
+            default:
+                throw new \Exception('@todo'); // @codeCoverageIgnore
+        }
+
+        return $result;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::getAddressType()
+     */
+    public function getAddressType()
+    {
+        return $this->fromAddress->getAddressType();
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::getStartAddress()
+     */
+    public function getStartAddress()
+    {
+        return $this->fromAddress;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::getEndAddress()
+     */
+    public function getEndAddress()
+    {
+        return $this->toAddress;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::getComparableStartString()
+     */
+    public function getComparableStartString()
+    {
+        return $this->fromAddress->getComparableString();
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::getComparableEndString()
+     */
+    public function getComparableEndString()
+    {
+        return $this->toAddress->getComparableString();
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::asSubnet()
+     */
+    public function asSubnet()
+    {
+        switch ($this->getAddressType()) {
+            case AddressType::T_IPv4:
+                return new Subnet($this->getStartAddress(), $this->getEndAddress(), 8 * (4 - $this->asterisksCount));
+            case AddressType::T_IPv6:
+                return new Subnet($this->getStartAddress(), $this->getEndAddress(), 16 * (8 - $this->asterisksCount));
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::asPattern()
+     */
+    public function asPattern()
+    {
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::getSubnetMask()
+     */
+    public function getSubnetMask()
+    {
+        if ($this->getAddressType() !== AddressType::T_IPv4) {
+            return null;
+        }
+        switch ($this->asterisksCount) {
+            case 0:
+                $bytes = array(255, 255, 255, 255);
+                break;
+            case 4:
+                $bytes = array(0, 0, 0, 0);
+                break;
+            default:
+                $bytes = array_pad(array_fill(0, 4 - $this->asterisksCount, 255), 4, 0);
+                break;
+        }
+
+        return IPv4::fromBytes($bytes);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::getReverseDNSLookupName()
+     */
+    public function getReverseDNSLookupName()
+    {
+        return $this->asterisksCount === 0 ? array($this->getStartAddress()->getReverseDNSLookupName()) : $this->asSubnet()->getReverseDNSLookupName();
+    }
+}

+ 120 - 0
vendor/mlocati/ip-lib/src/Range/RangeInterface.php

@@ -0,0 +1,120 @@
+<?php
+
+namespace IPLib\Range;
+
+use IPLib\Address\AddressInterface;
+
+/**
+ * Interface of all the range types.
+ */
+interface RangeInterface
+{
+    /**
+     * Get the short string representation of this address.
+     *
+     * @return string
+     */
+    public function __toString();
+
+    /**
+     * 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 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();
+
+    /**
+     * Get the subnet/CIDR representation of this range.
+     *
+     * @return \IPLib\Range\Subnet
+     */
+    public function asSubnet();
+
+    /**
+     * Get the pattern/asterisk representation (if applicable) of this range.
+     *
+     * @return \IPLib\Range\Pattern|null return NULL if this range can't be represented by a pattern notation
+     */
+    public function asPattern();
+
+    /**
+     * Get the Reverse DNS Lookup Addresses of this IP range.
+     *
+     * @return string[]
+     *
+     * @example for IPv4 it returns something like array('x.x.x.x.in-addr.arpa', 'x.x.x.x.in-addr.arpa') (where the number of 'x.' ranges from 1 to 4)
+     * @example for IPv6 it returns something like array('x.x.x.x..x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.ip6.arpa', 'x.x.x.x..x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.ip6.arpa') (where the number of 'x.' ranges from 1 to 32)
+     */
+    public function getReverseDNSLookupName();
+}

+ 226 - 0
vendor/mlocati/ip-lib/src/Range/Single.php

@@ -0,0 +1,226 @@
+<?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 extends AbstractRange
+{
+    /**
+     * @var \IPLib\Address\AddressInterface
+     */
+    protected $address;
+
+    /**
+     * Initializes the instance.
+     *
+     * @param \IPLib\Address\AddressInterface $address
+     */
+    protected function __construct(AddressInterface $address)
+    {
+        $this->address = $address;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::__toString()
+     */
+    public function __toString()
+    {
+        return $this->address->__toString();
+    }
+
+    /**
+     * Try get the range instance starting from its string representation.
+     *
+     * @param string|mixed $range
+     * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
+     *
+     * @return static|null
+     */
+    public static function fromString($range, $supportNonDecimalIPv4 = false)
+    {
+        $result = null;
+        $address = Factory::addressFromString($range, true, true, $supportNonDecimalIPv4);
+        if ($address !== null) {
+            $result = new static($address);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Create the range instance starting from an address instance.
+     *
+     * @param \IPLib\Address\AddressInterface $address
+     *
+     * @return static
+     */
+    public static function fromAddress(AddressInterface $address)
+    {
+        return new static($address);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::toString()
+     */
+    public function toString($long = false)
+    {
+        return $this->address->toString($long);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::getAddressType()
+     */
+    public function getAddressType()
+    {
+        return $this->address->getAddressType();
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::getRangeType()
+     */
+    public function getRangeType()
+    {
+        return $this->address->getRangeType();
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::contains()
+     */
+    public function contains(AddressInterface $address)
+    {
+        $result = false;
+        if ($address->getAddressType() === $this->getAddressType()) {
+            if ($address->toString(false) === $this->address->toString(false)) {
+                $result = true;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::containsRange()
+     */
+    public function containsRange(RangeInterface $range)
+    {
+        $result = false;
+        if ($range->getAddressType() === $this->getAddressType()) {
+            if ($range->toString(false) === $this->toString(false)) {
+                $result = true;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::getStartAddress()
+     */
+    public function getStartAddress()
+    {
+        return $this->address;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::getEndAddress()
+     */
+    public function getEndAddress()
+    {
+        return $this->address;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::getComparableStartString()
+     */
+    public function getComparableStartString()
+    {
+        return $this->address->getComparableString();
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::getComparableEndString()
+     */
+    public function getComparableEndString()
+    {
+        return $this->address->getComparableString();
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::asSubnet()
+     */
+    public function asSubnet()
+    {
+        $networkPrefixes = array(
+            AddressType::T_IPv4 => 32,
+            AddressType::T_IPv6 => 128,
+        );
+
+        return new Subnet($this->address, $this->address, $networkPrefixes[$this->address->getAddressType()]);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::asPattern()
+     */
+    public function asPattern()
+    {
+        return new Pattern($this->address, $this->address, 0);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::getSubnetMask()
+     */
+    public function getSubnetMask()
+    {
+        if ($this->getAddressType() !== AddressType::T_IPv4) {
+            return null;
+        }
+
+        return IPv4::fromBytes(array(255, 255, 255, 255));
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::getReverseDNSLookupName()
+     */
+    public function getReverseDNSLookupName()
+    {
+        return array($this->getStartAddress()->getReverseDNSLookupName());
+    }
+}

+ 305 - 0
vendor/mlocati/ip-lib/src/Range/Subnet.php

@@ -0,0 +1,305 @@
+<?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 extends AbstractRange
+{
+    /**
+     * 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;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::__toString()
+     */
+    public function __toString()
+    {
+        return $this->toString();
+    }
+
+    /**
+     * Try get the range instance starting from its string representation.
+     *
+     * @param string|mixed $range
+     * @param bool $supportNonDecimalIPv4 set to true to support parsing non decimal (that is, octal and hexadecimal) IPv4 addresses
+     *
+     * @return static|null
+     */
+    public static function fromString($range, $supportNonDecimalIPv4 = false)
+    {
+        if (!is_string($range)) {
+            return null;
+        }
+        $parts = explode('/', $range);
+        if (count($parts) !== 2) {
+            return null;
+        }
+        $address = Factory::addressFromString($parts[0], true, true, $supportNonDecimalIPv4);
+        if ($address === null) {
+            return null;
+        }
+        if (!preg_match('/^[0-9]{1,9}$/', $parts[1])) {
+            return null;
+        }
+        $networkPrefix = (int) $parts[1];
+        $addressBytes = $address->getBytes();
+        $totalBytes = count($addressBytes);
+        $numDifferentBits = $totalBytes * 8 - $networkPrefix;
+        if ($numDifferentBits < 0) {
+            return null;
+        }
+        $numSameBytes = $networkPrefix >> 3;
+        $sameBytes = array_slice($addressBytes, 0, $numSameBytes);
+        $differentBytesStart = ($totalBytes === $numSameBytes) ? array() : array_fill(0, $totalBytes - $numSameBytes, 0);
+        $differentBytesEnd = ($totalBytes === $numSameBytes) ? array() : array_fill(0, $totalBytes - $numSameBytes, 255);
+        $startSameBits = $networkPrefix % 8;
+        if ($startSameBits !== 0) {
+            $varyingByte = $addressBytes[$numSameBytes];
+            $differentBytesStart[0] = $varyingByte & bindec(str_pad(str_repeat('1', $startSameBits), 8, '0', STR_PAD_RIGHT));
+            $differentBytesEnd[0] = $differentBytesStart[0] + bindec(str_repeat('1', 8 - $startSameBits));
+        }
+
+        return new static(
+            Factory::addressFromBytes(array_merge($sameBytes, $differentBytesStart)),
+            Factory::addressFromBytes(array_merge($sameBytes, $differentBytesEnd)),
+            $networkPrefix
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::toString()
+     */
+    public function toString($long = false)
+    {
+        return $this->fromAddress->toString($long) . '/' . $this->networkPrefix;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::getAddressType()
+     */
+    public function getAddressType()
+    {
+        return $this->fromAddress->getAddressType();
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::getStartAddress()
+     */
+    public function getStartAddress()
+    {
+        return $this->fromAddress;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::getEndAddress()
+     */
+    public function getEndAddress()
+    {
+        return $this->toAddress;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::getComparableStartString()
+     */
+    public function getComparableStartString()
+    {
+        return $this->fromAddress->getComparableString();
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::getComparableEndString()
+     */
+    public function getComparableEndString()
+    {
+        return $this->toAddress->getComparableString();
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::asSubnet()
+     */
+    public function asSubnet()
+    {
+        return $this;
+    }
+
+    /**
+     * Get the pattern (asterisk) representation (if applicable) of this range.
+     *
+     * @return \IPLib\Range\Pattern|null return NULL if this range can't be represented by a pattern notation
+     */
+    public function asPattern()
+    {
+        $address = $this->getStartAddress();
+        $networkPrefix = $this->getNetworkPrefix();
+        switch ($address->getAddressType()) {
+            case AddressType::T_IPv4:
+                return $networkPrefix % 8 === 0 ? new Pattern($address, $address, 4 - $networkPrefix / 8) : null;
+            case AddressType::T_IPv6:
+                return $networkPrefix % 16 === 0 ? new Pattern($address, $address, 8 - $networkPrefix / 16) : null;
+        }
+    }
+
+    /**
+     * Get the 6to4 address IPv6 address range.
+     *
+     * @return self
+     */
+    public static function get6to4()
+    {
+        if (self::$sixToFour === null) {
+            self::$sixToFour = self::fromString('2002::/16');
+        }
+
+        return self::$sixToFour;
+    }
+
+    /**
+     * Get subnet prefix.
+     *
+     * @return int
+     */
+    public function getNetworkPrefix()
+    {
+        return $this->networkPrefix;
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::getSubnetMask()
+     */
+    public function getSubnetMask()
+    {
+        if ($this->getAddressType() !== AddressType::T_IPv4) {
+            return null;
+        }
+        $bytes = array();
+        $prefix = $this->getNetworkPrefix();
+        while ($prefix >= 8) {
+            $bytes[] = 255;
+            $prefix -= 8;
+        }
+        if ($prefix !== 0) {
+            $bytes[] = bindec(str_pad(str_repeat('1', $prefix), 8, '0'));
+        }
+        $bytes = array_pad($bytes, 4, 0);
+
+        return IPv4::fromBytes($bytes);
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * @see \IPLib\Range\RangeInterface::getReverseDNSLookupName()
+     */
+    public function getReverseDNSLookupName()
+    {
+        switch ($this->getAddressType()) {
+            case AddressType::T_IPv4:
+                $unitSize = 8; // bytes
+                $maxUnits = 4;
+                $isHex = false;
+                $rxUnit = '\d+';
+                break;
+            case AddressType::T_IPv6:
+                $unitSize = 4; // nibbles
+                $maxUnits = 32;
+                $isHex = true;
+                $rxUnit = '[0-9A-Fa-f]';
+                break;
+        }
+        $totBits = $unitSize * $maxUnits;
+        $prefixUnits = (int) ($this->networkPrefix / $unitSize);
+        $extraBits = ($totBits - $this->networkPrefix) % $unitSize;
+        if ($extraBits !== 0) {
+            $prefixUnits += 1;
+        }
+        $numVariants = 1 << $extraBits;
+        $result = array();
+        $unitsToRemove = $maxUnits - $prefixUnits;
+        $initialPointer = preg_replace("/^(({$rxUnit})\.){{$unitsToRemove}}/", '', $this->getStartAddress()->getReverseDNSLookupName());
+        $chunks = explode('.', $initialPointer, 2);
+        for ($index = 0; $index < $numVariants; $index++) {
+            if ($index !== 0) {
+                $chunks[0] = $isHex ? dechex(1 + hexdec($chunks[0])) : (string) (1 + (int) $chunks[0]);
+            }
+            $result[] = implode('.', $chunks);
+        }
+
+        return $result;
+    }
+}

+ 150 - 0
vendor/mlocati/ip-lib/src/Range/Type.php

@@ -0,0 +1,150 @@
+<?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;
+
+    /**
+     * Carrier-grade NAT address.
+     *
+     * @var int
+     */
+    const T_CGNAT = 14;
+
+    /**
+     * 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';
+            case static::T_CGNAT:
+                return 'Carrier-grade NAT';
+            default:
+                return $type === null ? 'Unknown type' : sprintf('Unknown type (%s)', $type);
+        }
+    }
+}

+ 118 - 0
vendor/mlocati/ip-lib/src/Service/BinaryMath.php

@@ -0,0 +1,118 @@
+<?php
+
+namespace IPLib\Service;
+
+/**
+ * Helper class to work with unsigned binary integers.
+ */
+class BinaryMath
+{
+    /**
+     * Trim the leading zeroes from a non-negative integer represented in binary form.
+     *
+     * @param string $value
+     *
+     * @return string
+     */
+    public function reduce($value)
+    {
+        $value = ltrim($value, '0');
+
+        return $value === '' ? '0' : $value;
+    }
+
+    /**
+     * Compare two non-negative integers represented in binary form.
+     *
+     * @param string $a
+     * @param string $b
+     *
+     * @return int 1 if $a is greater than $b, -1 if $b is greater than $b, 0 if they are the same
+     */
+    public function compare($a, $b)
+    {
+        list($a, $b) = $this->toSameLength($a, $b);
+
+        return $a < $b ? -1 : ($a > $b ? 1 : 0);
+    }
+
+    /**
+     * Add 1 to a non-negative integer represented in binary form.
+     *
+     * @param string $value
+     *
+     * @return string
+     */
+    public function increment($value)
+    {
+        $lastZeroIndex = strrpos($value, '0');
+        if ($lastZeroIndex === false) {
+            return '1' . str_repeat('0', strlen($value));
+        }
+
+        return ltrim(substr($value, 0, $lastZeroIndex), '0') . '1' . str_repeat('0', strlen($value) - $lastZeroIndex - 1);
+    }
+
+    /**
+     * Calculate the bitwise AND of two non-negative integers represented in binary form.
+     *
+     * @param string $operand1
+     * @param string $operand2
+     *
+     * @return string
+     */
+    public function andX($operand1, $operand2)
+    {
+        $operand1 = $this->reduce($operand1);
+        $operand2 = $this->reduce($operand2);
+        $numBits = min(strlen($operand1), strlen($operand2));
+        $operand1 = substr(str_pad($operand1, $numBits, '0', STR_PAD_LEFT), -$numBits);
+        $operand2 = substr(str_pad($operand2, $numBits, '0', STR_PAD_LEFT), -$numBits);
+        $result = '';
+        for ($index = 0; $index < $numBits; $index++) {
+            $result .= $operand1[$index] === '1' && $operand2[$index] === '1' ? '1' : '0';
+        }
+
+        return $this->reduce($result);
+    }
+
+    /**
+     * Calculate the bitwise OR of two non-negative integers represented in binary form.
+     *
+     * @param string $operand1
+     * @param string $operand2
+     *
+     * @return string
+     */
+    public function orX($operand1, $operand2)
+    {
+        list($operand1, $operand2, $numBits) = $this->toSameLength($operand1, $operand2);
+        $result = '';
+        for ($index = 0; $index < $numBits; $index++) {
+            $result .= $operand1[$index] === '1' || $operand2[$index] === '1' ? '1' : '0';
+        }
+
+        return $result;
+    }
+
+    /**
+     * Zero-padding of two non-negative integers represented in binary form, so that they have the same length.
+     *
+     * @param string $num1
+     * @param string $num2
+     *
+     * @return string[],int[] The first array element is $num1 (padded), the first array element is $num2 (padded), the third array element is the number of bits
+     */
+    private function toSameLength($num1, $num2)
+    {
+        $num1 = $this->reduce($num1);
+        $num2 = $this->reduce($num2);
+        $numBits = max(strlen($num1), strlen($num2));
+
+        return array(
+            str_pad($num1, $numBits, '0', STR_PAD_LEFT),
+            str_pad($num2, $numBits, '0', STR_PAD_LEFT),
+            $numBits,
+        );
+    }
+}

+ 161 - 0
vendor/mlocati/ip-lib/src/Service/RangesFromBounradyCalculator.php

@@ -0,0 +1,161 @@
+<?php
+
+namespace IPLib\Service;
+
+use IPLib\Address\AddressInterface;
+use IPLib\Factory;
+use IPLib\Range\Subnet;
+
+/**
+ * Helper class to calculate the subnets describing all (and only all) the addresses between two bouundaries.
+ */
+class RangesFromBounradyCalculator
+{
+    /**
+     * The BinaryMath instance to be used to perform bitwise poerations.
+     *
+     * @var \IPLib\Service\BinaryMath
+     */
+    private $math;
+
+    /**
+     * The number of bits used to represent addresses.
+     *
+     * @var int
+     *
+     * @example 32 for IPv4, 128 for IPv6
+     */
+    private $numBits;
+
+    /**
+     * The bit masks for every bit index.
+     *
+     * @var string[]
+     */
+    private $masks;
+
+    /**
+     * The bit unmasks for every bit index.
+     *
+     * @var string[]
+     */
+    private $unmasks;
+
+    /**
+     * Initializes the instance.
+     *
+     * @param int $numBits the number of bits used to represent addresses (32 for IPv4, 128 for IPv6)
+     */
+    public function __construct($numBits)
+    {
+        $this->math = new BinaryMath();
+        $this->setNumBits($numBits);
+    }
+
+    /**
+     * Calculate the subnets describing all (and only all) the addresses between two bouundaries.
+     *
+     * @param \IPLib\Address\AddressInterface $from
+     * @param \IPLib\Address\AddressInterface $to
+     *
+     * @return \IPLib\Range\Subnet[]|null return NULL if the two addresses have an invalid number of bits (that is, different from the one passed to the constructor of this class)
+     */
+    public function getRanges(AddressInterface $from, AddressInterface $to)
+    {
+        if ($from->getNumberOfBits() !== $this->numBits || $to->getNumberOfBits() !== $this->numBits) {
+            return null;
+        }
+        if ($from->getComparableString() > $to->getComparableString()) {
+            list($from, $to) = array($to, $from);
+        }
+        $result = array();
+        $this->calculate($this->math->reduce($from->getBits()), $this->math->reduce($to->getBits()), $this->numBits, $result);
+
+        return $result;
+    }
+
+    /**
+     * Set the number of bits used to represent addresses (32 for IPv4, 128 for IPv6).
+     *
+     * @param int $numBits
+     */
+    private function setNumBits($numBits)
+    {
+        $numBits = (int) $numBits;
+        $masks = array();
+        $unmasks = array();
+        for ($bit = 0; $bit < $numBits; $bit++) {
+            $masks[$bit] = str_repeat('1', $numBits - $bit) . str_repeat('0', $bit);
+            $unmasks[$bit] = $bit === 0 ? '0' : str_repeat('1', $bit);
+        }
+        $this->numBits = $numBits;
+        $this->masks = $masks;
+        $this->unmasks = $unmasks;
+    }
+
+    /**
+     * Calculate the subnets.
+     *
+     * @param string $start the start address (represented in reduced bit form)
+     * @param string $end the end address (represented in reduced bit form)
+     * @param int $position the number of bits in the mask we are comparing at this cycle
+     * @param \IPLib\Range\Subnet[] $result found ranges will be added to this variable
+     */
+    private function calculate($start, $end, $position, array &$result)
+    {
+        if ($start === $end) {
+            $result[] = $this->subnetFromBits($start, $this->numBits);
+
+            return;
+        }
+        for ($index = $position - 1; $index >= 0; $index--) {
+            $startMasked = $this->math->andX($start, $this->masks[$index]);
+            $endMasked = $this->math->andX($end, $this->masks[$index]);
+            if ($startMasked !== $endMasked) {
+                $position = $index;
+                break;
+            }
+        }
+        if ($startMasked === $start && $this->math->andX($this->math->increment($end), $this->unmasks[$position]) === '0') {
+            $result[] = $this->subnetFromBits($start, $this->numBits - 1 - $position);
+
+            return;
+        }
+        $middleAddress = $this->math->orX($start, $this->unmasks[$position]);
+        $this->calculate($start, $middleAddress, $position, $result);
+        $this->calculate($this->math->increment($middleAddress), $end, $position, $result);
+    }
+
+    /**
+     * Create an address instance starting from its bits.
+     *
+     * @param string $bits the bits of the address (represented in reduced bit form)
+     *
+     * @return \IPLib\Address\AddressInterface
+     */
+    private function addressFromBits($bits)
+    {
+        $bits = str_pad($bits, $this->numBits, '0', STR_PAD_LEFT);
+        $bytes = array();
+        foreach (explode("\n", trim(chunk_split($bits, 8, "\n"))) as $byteBits) {
+            $bytes[] = bindec($byteBits);
+        }
+
+        return Factory::addressFromBytes($bytes);
+    }
+
+    /**
+     * Create an range instance starting from the bits if the address and the length of the network prefix.
+     *
+     * @param string $bits the bits of the address (represented in reduced bit form)
+     * @param int $networkPrefix the length of the network prefix
+     *
+     * @return \IPLib\Range\Subnet
+     */
+    private function subnetFromBits($bits, $networkPrefix)
+    {
+        $address = $this->addressFromBits($bits);
+
+        return new Subnet($address, $address, $networkPrefix);
+    }
+}