Phase 6: AIOS security plugin with conservative login lockdown config (10 attempts)
This commit is contained in:
Vendored
Executable
+185
@@ -0,0 +1,185 @@
|
||||
<?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
|
||||
*
|
||||
* @since 1.14.0
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @since 1.14.0
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @phpstan-return \IPLib\Address\Type::T_IPv4|\IPLib\Address\Type::T_IPv6
|
||||
*/
|
||||
public function getAddressType();
|
||||
|
||||
/**
|
||||
* Get the default RFC reserved range type.
|
||||
*
|
||||
* @return int One of the \IPLib\Range\Type::T_... constants
|
||||
*
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static function getDefaultReservedRangeType();
|
||||
|
||||
/**
|
||||
* Get the RFC reserved ranges (except the ones of type getDefaultReservedRangeType).
|
||||
*
|
||||
* @return \IPLib\Address\AssignedRange[] ranges are sorted
|
||||
*
|
||||
* @since 1.5.0
|
||||
*/
|
||||
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 at a certain distance from this address.
|
||||
*
|
||||
* @param int|numeric-string|mixed $n the distance of the address (can be negative)
|
||||
*
|
||||
* @return \IPLib\Address\AddressInterface|null return NULL if $n is not an integer or NULL if $n is neither an integer nor a string containing a valid integer, or if the final address would be invalid
|
||||
*
|
||||
* @since 1.15.0
|
||||
* @since 1.21.0 $n can also be a numeric string
|
||||
*
|
||||
* @example passing 1 to the address 127.0.0.1 will result in 127.0.0.2
|
||||
* @example passing -1 to the address 127.0.0.1 will result in 127.0.0.0
|
||||
* @example passing -1 to the address 0.0.0.0 will result in NULL
|
||||
*/
|
||||
public function getAddressAtOffset($n);
|
||||
|
||||
/**
|
||||
* Get the address right after this IP address (if available).
|
||||
*
|
||||
* @return \IPLib\Address\AddressInterface|null
|
||||
*
|
||||
* @see \IPLib\Address\AddressInterface::getAddressAtOffset()
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public function getNextAddress();
|
||||
|
||||
/**
|
||||
* Get the address right before this IP address (if available).
|
||||
*
|
||||
* @return \IPLib\Address\AddressInterface|null
|
||||
*
|
||||
* @see \IPLib\Address\AddressInterface::getAddressAtOffset()
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public function getPreviousAddress();
|
||||
|
||||
/**
|
||||
* Get the Reverse DNS Lookup Address of this IP address.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 1.12.0
|
||||
*
|
||||
* @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();
|
||||
|
||||
/**
|
||||
* Shift the bits of the address, padding with zeroes.
|
||||
*
|
||||
* @param int $bits If negative the bits will be shifted left, if positive the bits will be shifted right
|
||||
*
|
||||
* @return self
|
||||
*
|
||||
* @since 1.20.0
|
||||
*
|
||||
* @example shifting by 1 127.0.0.1 you'll have 63.128.0.0
|
||||
* @example shifting by -1 127.0.0.1 you'll have 254.0.0.2
|
||||
*/
|
||||
public function shift($bits);
|
||||
|
||||
/**
|
||||
* Create a new IP address by adding to this address another address.
|
||||
*
|
||||
* @return self|null returns NULL if $other is not compatible with this address, or if it generates an invalid address
|
||||
*
|
||||
* @since 1.20.0
|
||||
*
|
||||
* @example adding 0.0.0.10 to 127.0.0.1 generates the IP 127.0.0.11
|
||||
* @example adding 255.0.0.10 to 127.0.0.1 generates NULL
|
||||
*/
|
||||
public function add(AddressInterface $other);
|
||||
}
|
||||
Vendored
Executable
+140
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
namespace IPLib\Address;
|
||||
|
||||
use IPLib\Range\RangeInterface;
|
||||
|
||||
/**
|
||||
* Represents an IP address range with an assigned range type.
|
||||
*
|
||||
* @since 1.5.0
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
Vendored
Executable
+582
@@ -0,0 +1,582 @@
|
||||
<?php
|
||||
|
||||
namespace IPLib\Address;
|
||||
|
||||
use IPLib\ParseStringFlag;
|
||||
use IPLib\Range\RangeInterface;
|
||||
use IPLib\Range\Subnet;
|
||||
use IPLib\Range\Type as RangeType;
|
||||
use IPLib\Service\BinaryMath;
|
||||
use IPLib\Service\NumberInChunks;
|
||||
|
||||
/**
|
||||
* An IPv4 address.
|
||||
*
|
||||
* @phpstan-consistent-constructor
|
||||
*/
|
||||
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|null
|
||||
*/
|
||||
protected $comparableString;
|
||||
|
||||
/**
|
||||
* An array containing RFC designated address ranges.
|
||||
*
|
||||
* @var \IPLib\Address\AssignedRange[]|null
|
||||
*/
|
||||
private static $reservedRanges;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.17.0: use the parseString() method instead.
|
||||
* For upgrading:
|
||||
* - if $mayIncludePort is true, use the ParseStringFlag::MAY_INCLUDE_PORT flag
|
||||
* - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
|
||||
*
|
||||
* @param string|mixed $address the address to parse
|
||||
* @param bool $mayIncludePort
|
||||
* @param bool $supportNonDecimalIPv4
|
||||
*
|
||||
* @return static|null
|
||||
*
|
||||
* @see \IPLib\Address\IPv4::parseString()
|
||||
* @since 1.1.0 added the $mayIncludePort argument
|
||||
* @since 1.10.0 added the $supportNonDecimalIPv4 argument
|
||||
*/
|
||||
public static function fromString($address, $mayIncludePort = true, $supportNonDecimalIPv4 = false)
|
||||
{
|
||||
return static::parseString($address, 0 | ($mayIncludePort ? ParseStringFlag::MAY_INCLUDE_PORT : 0) | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 int $flags A combination or zero or more flags
|
||||
*
|
||||
* @return static|null
|
||||
*
|
||||
* @see \IPLib\ParseStringFlag
|
||||
* @since 1.17.0
|
||||
*/
|
||||
public static function parseString($address, $flags = 0)
|
||||
{
|
||||
if (!is_string($address)) {
|
||||
return null;
|
||||
}
|
||||
$flags = (int) $flags;
|
||||
$matches = null;
|
||||
if ($flags & ParseStringFlag::ADDRESS_MAYBE_RDNS) {
|
||||
if (preg_match('/^([12]?[0-9]{1,2}\.[12]?[0-9]{1,2}\.[12]?[0-9]{1,2}\.[12]?[0-9]{1,2})\.in-addr\.arpa\.?$/i', $address, $matches)) {
|
||||
$address = implode('.', array_reverse(explode('.', $matches[1])));
|
||||
$flags = $flags & ~(ParseStringFlag::IPV4_MAYBE_NON_DECIMAL | ParseStringFlag::IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED);
|
||||
}
|
||||
}
|
||||
if ($flags & ParseStringFlag::IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED) {
|
||||
if (strpos($address, '.') === 0) {
|
||||
return null;
|
||||
}
|
||||
$lengthNonHex = '{1,11}';
|
||||
$lengthHex = '{1,8}';
|
||||
$chunk234Optional = true;
|
||||
} else {
|
||||
if (!strpos($address, '.')) {
|
||||
return null;
|
||||
}
|
||||
$lengthNonHex = '{1,3}';
|
||||
$lengthHex = '{1,2}';
|
||||
$chunk234Optional = false;
|
||||
}
|
||||
$rxChunk1 = "0?[0-9]{$lengthNonHex}";
|
||||
if ($flags & ParseStringFlag::IPV4_MAYBE_NON_DECIMAL) {
|
||||
$rxChunk1 = "(?:0[Xx]0*[0-9A-Fa-f]{$lengthHex})|(?:{$rxChunk1})";
|
||||
$onlyDecimal = false;
|
||||
} else {
|
||||
$onlyDecimal = true;
|
||||
}
|
||||
$rxChunk1 = "0*?({$rxChunk1})";
|
||||
$rxChunk234 = "\.{$rxChunk1}";
|
||||
if ($chunk234Optional) {
|
||||
$rxChunk234 = "(?:{$rxChunk234})?";
|
||||
}
|
||||
$rx = "{$rxChunk1}{$rxChunk234}{$rxChunk234}{$rxChunk234}";
|
||||
if ($flags & ParseStringFlag::MAY_INCLUDE_PORT) {
|
||||
$rx .= '(?::\d+)?';
|
||||
}
|
||||
if (!preg_match('/^' . $rx . '$/', $address, $matches)) {
|
||||
return null;
|
||||
}
|
||||
$math = new \IPLib\Service\UnsignedIntegerMath();
|
||||
$nums = array();
|
||||
$maxChunkIndex = count($matches) - 1;
|
||||
for ($i = 1; $i <= $maxChunkIndex; $i++) {
|
||||
$numBytes = $i === $maxChunkIndex ? 5 - $i : 1;
|
||||
$chunkBytes = $math->getBytes($matches[$i], $numBytes, $onlyDecimal);
|
||||
if ($chunkBytes === null) {
|
||||
return null;
|
||||
}
|
||||
$nums = array_merge($nums, $chunkBytes);
|
||||
}
|
||||
|
||||
return new static(implode('.', $nums));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an array of bytes and returns an IPv4 instance if the array is valid, or null otherwise.
|
||||
*
|
||||
* @param array<int|mixed> $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
|
||||
*
|
||||
* @since 1.10.0
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @since 1.10.0
|
||||
*
|
||||
* @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) {
|
||||
$subnet = Subnet::parseString($exceptionRange);
|
||||
/** @var Subnet $subnet */
|
||||
$exceptions[] = new AssignedRange($subnet, $exceptionType);
|
||||
}
|
||||
}
|
||||
$subnet = Subnet::parseString($range);
|
||||
/** @var Subnet $subnet */
|
||||
$reservedRanges[] = new AssignedRange($subnet, $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();
|
||||
$ipv6 = IPv6::parseString('2002:' . sprintf('%02x', $myBytes[0]) . sprintf('%02x', $myBytes[1]) . ':' . sprintf('%02x', $myBytes[2]) . sprintf('%02x', $myBytes[3]) . '::');
|
||||
/** @var IPv6 $ipv6 */
|
||||
|
||||
return $ipv6;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an IPv6 representation of this address (in IPv6 IPv4-mapped notation).
|
||||
*
|
||||
* @return \IPLib\Address\IPv6
|
||||
*
|
||||
* @since 1.11.0
|
||||
*/
|
||||
public function toIPv6IPv4Mapped()
|
||||
{
|
||||
$ipv6 = IPv6::fromBytes(array_merge(array(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff), $this->getBytes()));
|
||||
/** @var IPv6 $ipv6 */
|
||||
|
||||
return $ipv6;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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::getAddressAtOffset()
|
||||
*/
|
||||
public function getAddressAtOffset($n)
|
||||
{
|
||||
if (is_int($n)) {
|
||||
$thatChunks = NumberInChunks::fromInteger($n, NumberInChunks::CHUNKSIZE_BYTES);
|
||||
} elseif (($s = BinaryMath::getInstance()->normalizeIntegerString($n)) !== '') {
|
||||
$thatChunks = NumberInChunks::fromNumericString($s, NumberInChunks::CHUNKSIZE_BYTES);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
$myBytes = $this->getBytes();
|
||||
while (isset($myBytes[1]) && $myBytes[0] === 0) {
|
||||
array_shift($myBytes);
|
||||
}
|
||||
$myChunks = new NumberInChunks(false, $myBytes, NumberInChunks::CHUNKSIZE_BYTES);
|
||||
$result = $myChunks->add($thatChunks);
|
||||
if ($result->negative || count($result->chunks) > 4) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return static::fromBytes(array_pad($result->chunks, -4, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \IPLib\Address\AddressInterface::getNextAddress()
|
||||
*/
|
||||
public function getNextAddress()
|
||||
{
|
||||
return $this->getAddressAtOffset(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \IPLib\Address\AddressInterface::getPreviousAddress()
|
||||
*/
|
||||
public function getPreviousAddress()
|
||||
{
|
||||
return $this->getAddressAtOffset(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \IPLib\Address\AddressInterface::getReverseDNSLookupName()
|
||||
*/
|
||||
public function getReverseDNSLookupName()
|
||||
{
|
||||
return implode(
|
||||
'.',
|
||||
array_reverse($this->getBytes())
|
||||
) . '.in-addr.arpa';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \IPLib\Address\AddressInterface::shift()
|
||||
*/
|
||||
public function shift($bits)
|
||||
{
|
||||
$bits = (int) $bits;
|
||||
if ($bits === 0) {
|
||||
return $this;
|
||||
}
|
||||
$absBits = abs($bits);
|
||||
if ($absBits >= 32) {
|
||||
return new self('0.0.0.0');
|
||||
}
|
||||
$pad = str_repeat('0', $absBits);
|
||||
$paddedBits = $this->getBits();
|
||||
if ($bits > 0) {
|
||||
$paddedBits = $pad . substr($paddedBits, 0, -$bits);
|
||||
} else {
|
||||
$paddedBits = substr($paddedBits, $absBits) . $pad;
|
||||
}
|
||||
$bytes = array_map('bindec', str_split($paddedBits, 8));
|
||||
|
||||
return new static(implode('.', $bytes));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \IPLib\Address\AddressInterface::add()
|
||||
*/
|
||||
public function add(AddressInterface $other)
|
||||
{
|
||||
if (!$other instanceof self) {
|
||||
return null;
|
||||
}
|
||||
$myBytes = $this->getBytes();
|
||||
$otherBytes = $other->getBytes();
|
||||
$sum = array_fill(0, 4, 0);
|
||||
$carry = 0;
|
||||
for ($index = 3; $index >= 0; $index--) {
|
||||
$byte = $myBytes[$index] + $otherBytes[$index] + $carry;
|
||||
if ($byte > 0xFF) {
|
||||
$carry = $byte >> 8;
|
||||
$byte &= 0xFF;
|
||||
} else {
|
||||
$carry = 0;
|
||||
}
|
||||
$sum[$index] = $byte;
|
||||
}
|
||||
if ($carry !== 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new static(implode('.', $sum));
|
||||
}
|
||||
}
|
||||
Vendored
Executable
+679
@@ -0,0 +1,679 @@
|
||||
<?php
|
||||
|
||||
namespace IPLib\Address;
|
||||
|
||||
use IPLib\ParseStringFlag;
|
||||
use IPLib\Range\RangeInterface;
|
||||
use IPLib\Range\Subnet;
|
||||
use IPLib\Range\Type as RangeType;
|
||||
use IPLib\Service\BinaryMath;
|
||||
use IPLib\Service\NumberInChunks;
|
||||
|
||||
/**
|
||||
* An IPv6 address.
|
||||
*
|
||||
* @phpstan-consistent-constructor
|
||||
*/
|
||||
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 \IPLib\Address\AssignedRange[]|null
|
||||
*/
|
||||
private static $reservedRanges;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.17.0: use the parseString() method instead.
|
||||
* For upgrading:
|
||||
* - if $mayIncludePort is true, use the ParseStringFlag::MAY_INCLUDE_PORT flag
|
||||
* - if $mayIncludeZoneID is true, use the ParseStringFlag::MAY_INCLUDE_ZONEID flag
|
||||
*
|
||||
* @param string|mixed $address
|
||||
* @param bool $mayIncludePort
|
||||
* @param bool $mayIncludeZoneID
|
||||
*
|
||||
* @return static|null
|
||||
*
|
||||
* @see \IPLib\Address\IPv6::parseString()
|
||||
* @since 1.1.0 added the $mayIncludePort argument
|
||||
* @since 1.3.0 added the $mayIncludeZoneID argument
|
||||
*/
|
||||
public static function fromString($address, $mayIncludePort = true, $mayIncludeZoneID = true)
|
||||
{
|
||||
return static::parseString($address, 0 | ($mayIncludePort ? ParseStringFlag::MAY_INCLUDE_PORT : 0) | ($mayIncludeZoneID ? ParseStringFlag::MAY_INCLUDE_ZONEID : 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 int $flags A combination or zero or more flags
|
||||
*
|
||||
* @return static|null
|
||||
*
|
||||
* @see \IPLib\ParseStringFlag
|
||||
* @since 1.17.0
|
||||
*/
|
||||
public static function parseString($address, $flags = 0)
|
||||
{
|
||||
if (!is_string($address)) {
|
||||
return null;
|
||||
}
|
||||
$matches = null;
|
||||
$flags = (int) $flags;
|
||||
if ($flags & ParseStringFlag::ADDRESS_MAYBE_RDNS) {
|
||||
if (preg_match('/^([0-9a-f](?:\.[0-9a-f]){31})\.ip6\.arpa\.?/i', $address, $matches)) {
|
||||
$nibbles = array_reverse(explode('.', $matches[1]));
|
||||
$quibbles = array();
|
||||
foreach (array_chunk($nibbles, 4) as $n) {
|
||||
$quibbles[] = implode('', $n);
|
||||
}
|
||||
$address = implode(':', $quibbles);
|
||||
}
|
||||
}
|
||||
$result = null;
|
||||
if (strpos($address, ':') !== false && strpos($address, ':::') === false) {
|
||||
if ($flags & ParseStringFlag::MAY_INCLUDE_PORT && $address[0] === '[' && preg_match('/^\[(.+)]:\d+$/', $address, $matches)) {
|
||||
$address = $matches[1];
|
||||
}
|
||||
if ($flags & ParseStringFlag::MAY_INCLUDE_ZONEID) {
|
||||
$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::parseString($matches[1] . '0:0');
|
||||
if ($address6 !== null) {
|
||||
$address4 = IPv4::parseString($matches[2]);
|
||||
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 array<int|mixed> $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 array<int|mixed> $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 (int) 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) {
|
||||
$subnet = Subnet::parseString($exceptionRange);
|
||||
/** @var Subnet $subnet */
|
||||
$exceptions[] = new AssignedRange($subnet, $exceptionType);
|
||||
}
|
||||
}
|
||||
$subnet = Subnet::parseString($range);
|
||||
/** @var Subnet $subnet */
|
||||
$reservedRanges[] = new AssignedRange($subnet, $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.
|
||||
* @since 1.9.0
|
||||
*/
|
||||
public function toMixedIPv6IPv4String($ipV6Long = false, $ipV4Long = false)
|
||||
{
|
||||
$myBytes = $this->getBytes();
|
||||
$ipv6Bytes = array_merge(array_slice($myBytes, 0, 12), array(0xff, 0xff, 0xff, 0xff));
|
||||
$ipv6 = static::fromBytes($ipv6Bytes);
|
||||
/** @var IPv6 $ipv6 */
|
||||
$ipv6String = $ipv6->toString($ipV6Long);
|
||||
$ipv4Bytes = array_slice($myBytes, 12, 4);
|
||||
$ipv4 = IPv4::fromBytes($ipv4Bytes);
|
||||
/** @var IPv4 $ipv4 */
|
||||
$ipv4String = $ipv4->toString($ipV4Long);
|
||||
$result = preg_replace('/((ffff:ffff)|(\d+(\.\d+){3}))$/i', $ipv4String, $ipv6String);
|
||||
/** @var string $result */
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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::getAddressAtOffset()
|
||||
*/
|
||||
public function getAddressAtOffset($n)
|
||||
{
|
||||
if (is_int($n)) {
|
||||
$thatChunks = NumberInChunks::fromInteger($n, NumberInChunks::CHUNKSIZE_WORDS);
|
||||
} elseif (($s = BinaryMath::getInstance()->normalizeIntegerString($n)) !== '') {
|
||||
$thatChunks = NumberInChunks::fromNumericString($s, NumberInChunks::CHUNKSIZE_WORDS);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
$myWords = $this->getWords();
|
||||
while (isset($myWords[1]) && $myWords[0] === 0) {
|
||||
array_shift($myWords);
|
||||
}
|
||||
$myChunks = new NumberInChunks(false, $myWords, NumberInChunks::CHUNKSIZE_WORDS);
|
||||
$result = $myChunks->add($thatChunks);
|
||||
if ($result->negative || count($result->chunks) > 8) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return static::fromWords(array_pad($result->chunks, -8, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \IPLib\Address\AddressInterface::getNextAddress()
|
||||
*/
|
||||
public function getNextAddress()
|
||||
{
|
||||
return $this->getAddressAtOffset(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \IPLib\Address\AddressInterface::getPreviousAddress()
|
||||
*/
|
||||
public function getPreviousAddress()
|
||||
{
|
||||
return $this->getAddressAtOffset(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \IPLib\Address\AddressInterface::getReverseDNSLookupName()
|
||||
*/
|
||||
public function getReverseDNSLookupName()
|
||||
{
|
||||
return implode(
|
||||
'.',
|
||||
array_reverse(str_split(str_replace(':', '', $this->toString(true)), 1))
|
||||
) . '.ip6.arpa';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \IPLib\Address\AddressInterface::shift()
|
||||
*/
|
||||
public function shift($bits)
|
||||
{
|
||||
$bits = (int) $bits;
|
||||
if ($bits === 0) {
|
||||
return $this;
|
||||
}
|
||||
$absBits = abs($bits);
|
||||
if ($absBits >= 128) {
|
||||
return new self('0000:0000:0000:0000:0000:0000:0000:0000');
|
||||
}
|
||||
$pad = str_repeat('0', $absBits);
|
||||
$paddedBits = $this->getBits();
|
||||
if ($bits > 0) {
|
||||
$paddedBits = $pad . substr($paddedBits, 0, -$bits);
|
||||
} else {
|
||||
$paddedBits = substr($paddedBits, $absBits) . $pad;
|
||||
}
|
||||
$bytes = array_map('bindec', str_split($paddedBits, 16));
|
||||
/** @var int[] $bytes */
|
||||
$result = static::fromWords($bytes);
|
||||
/** @var IPv6 $result */
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \IPLib\Address\AddressInterface::add()
|
||||
*/
|
||||
public function add(AddressInterface $other)
|
||||
{
|
||||
if (!$other instanceof self) {
|
||||
return null;
|
||||
}
|
||||
$myWords = $this->getWords();
|
||||
$otherWords = $other->getWords();
|
||||
$sum = array_fill(0, 8, 0);
|
||||
$carry = 0;
|
||||
for ($index = 7; $index >= 0; $index--) {
|
||||
$word = $myWords[$index] + $otherWords[$index] + $carry;
|
||||
if ($word > 0xFFFF) {
|
||||
$carry = $word >> 16;
|
||||
$word &= 0xFFFF;
|
||||
} else {
|
||||
$carry = 0;
|
||||
}
|
||||
$sum[$index] = $word;
|
||||
}
|
||||
if ($carry !== 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return static::fromWords($sum);
|
||||
}
|
||||
}
|
||||
Vendored
Executable
+44
@@ -0,0 +1,44 @@
|
||||
<?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|mixed $type
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public static function getName($type)
|
||||
{
|
||||
switch ($type) {
|
||||
case static::T_IPv4:
|
||||
return 'IP v4';
|
||||
case static::T_IPv6:
|
||||
return 'IP v6';
|
||||
default:
|
||||
return $type === null ? 'Unknown type' : sprintf('Unknown type (%s)', print_r($type, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user