Phase 6: AIOS security plugin with conservative login lockdown config (10 attempts)
This commit is contained in:
Vendored
Executable
+20
@@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2016 Michele Locati
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
Vendored
Executable
+823
@@ -0,0 +1,823 @@
|
||||
[](https://github.com/mlocati/ip-lib/actions/workflows/tests.yml)
|
||||
[](https://coveralls.io/github/mlocati/ip-lib?branch=main)
|
||||
[](https://packagist.org/packages/mlocati/ip-lib)
|
||||
[](https://gitpod.io/#https://github.com/mlocati/ip-lib)
|
||||
|
||||
# IPLib - Handle IPv4, IPv6 and IP ranges
|
||||
|
||||
## Introduction
|
||||
|
||||
IPLib is a modern, PSR-compliant, test-driven IP addresses and subnets manipulation library. It implements primitives to handle IPv4 and IPv6 addresses, as well as IP ranges (subnets), in CIDR format (like `::1/128` or `127.0.0.1/32`) and in pattern format (like `::*:*` or `127.0.*.*`).
|
||||
|
||||
## Requirements
|
||||
|
||||
IPLib has very basic requirements as:
|
||||
|
||||
- Works with any PHP version greater than 5.3.3 (PHP **5.3.x**, **5.4.x**, **5.5.x**, **5.6.x**, **7.x**, and **8.x** are fully supported).
|
||||
- **No external dependencies**
|
||||
- **No special PHP configuration needed** (yes, it will __always work__ even if PHP has not been built with IPv6 support!).
|
||||
|
||||
## Installation
|
||||
|
||||
### Manual installation
|
||||
|
||||
[Download](https://github.com/mlocati/ip-lib/releases) the latest version, unzip it and add these lines in our PHP files:
|
||||
|
||||
```php
|
||||
require_once 'path/to/iplib/ip-lib.php';
|
||||
```
|
||||
|
||||
### Installation with Composer
|
||||
|
||||
Simply run
|
||||
|
||||
```sh
|
||||
composer require mlocati/ip-lib
|
||||
```
|
||||
|
||||
or add these lines to your `composer.json` file:
|
||||
|
||||
```json
|
||||
"require": {
|
||||
"mlocati/ip-lib": "^1"
|
||||
}
|
||||
```
|
||||
|
||||
## Sample usage
|
||||
|
||||
### Parse an address
|
||||
|
||||
To parse an IPv4 address:
|
||||
|
||||
```php
|
||||
$address = \IPLib\Address\IPv4::parseString('127.0.0.1');
|
||||
```
|
||||
|
||||
To parse an IPv6 address:
|
||||
|
||||
```php
|
||||
$address = \IPLib\Address\IPv6::parseString('::1');
|
||||
```
|
||||
|
||||
To parse an address in any format (IPv4 or IPv6):
|
||||
|
||||
```php
|
||||
$address = \IPLib\Factory::parseAddressString('::1');
|
||||
$address = \IPLib\Factory::parseAddressString('127.0.0.1');
|
||||
```
|
||||
|
||||
### Get the next/previous addresses
|
||||
|
||||
```php
|
||||
$address = \IPLib\Factory::parseAddressString('::1');
|
||||
|
||||
// This will print ::
|
||||
echo (string) $address->getPreviousAddress();
|
||||
|
||||
// This will print ::2
|
||||
echo (string) $address->getNextAddress();
|
||||
```
|
||||
|
||||
### Shifting the bits of an address
|
||||
|
||||
You can use the `shift` method to shift the address bits to the right (with positive values) or to the left (negative values):
|
||||
|
||||
```php
|
||||
$address = \IPLib\Factory::parseAddressString('2.4.8.16');
|
||||
// This will print 1.2.4.8
|
||||
echo (string) $address->shift(1);
|
||||
// This will print 4.8.16.32
|
||||
echo (string) $address->shift(-1);
|
||||
// This will print 4.8.16.0
|
||||
echo (string) $address->shift(-8);
|
||||
|
||||
$address = \IPLib\Factory::parseAddressString('::10');
|
||||
// This will print ::8
|
||||
echo (string) $address->shift(1);
|
||||
// This will print ::20
|
||||
echo (string) $address->shift(-1);
|
||||
// This will print ::10:0
|
||||
echo (string) $address->shift(-16);
|
||||
```
|
||||
|
||||
### Adding two IP addresses
|
||||
|
||||
You can calculate the sum of 2 IP addresses using the `add` method:
|
||||
|
||||
```php
|
||||
$a = \IPLib\Factory::parseAddressString('1.2.3.4');
|
||||
$b = \IPLib\Factory::parseAddressString('10.0.0.0');
|
||||
// This will print 11.2.3.4
|
||||
echo (string) $a->add($b);
|
||||
```
|
||||
|
||||
### Get the addresses at a specified offset
|
||||
|
||||
For addresses:
|
||||
|
||||
```php
|
||||
$address = \IPLib\Factory::parseAddressString('::1');
|
||||
|
||||
// This will print ::1
|
||||
echo (string) $address->getAddressAtOffset(0);
|
||||
|
||||
// This will print ::2
|
||||
echo (string) $address->getAddressAtOffset(1);
|
||||
|
||||
// This will print ::3
|
||||
echo (string) $address->getAddressAtOffset(2);
|
||||
|
||||
// This will print ::3e9
|
||||
echo (string) $address->getAddressAtOffset(1000);
|
||||
|
||||
// This will print ::
|
||||
echo (string) $address->getAddressAtOffset(-1);
|
||||
|
||||
// This will print NULL
|
||||
echo var_dump($address->getAddressAtOffset(-2));
|
||||
|
||||
// This will print ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
|
||||
echo (string) $address->getAddressAtOffset('340282366920938463463374607431768211454');
|
||||
|
||||
```
|
||||
|
||||
For ranges:
|
||||
|
||||
```php
|
||||
$range = \IPLib\Factory::parseRangeString('::ff00/120');
|
||||
|
||||
// This will print ::ff00
|
||||
echo (string) $range->getAddressAtOffset(0);
|
||||
|
||||
// This will print ::ff10
|
||||
echo (string) $range->getAddressAtOffset(16);
|
||||
|
||||
// This will print ::ff64
|
||||
echo (string) $range->getAddressAtOffset(100);
|
||||
|
||||
// This will print NULL because the address ::1:0 is out of the range
|
||||
var_dump($range->getAddressAtOffset(256));
|
||||
|
||||
// This will print ::ffff
|
||||
echo (string) $range->getAddressAtOffset(-1);
|
||||
|
||||
// This will print ::fff0
|
||||
echo (string) $range->getAddressAtOffset(-16);
|
||||
|
||||
// This will print ::ff00
|
||||
echo (string) $range->getAddressAtOffset(-256);
|
||||
|
||||
// This will print NULL because the address ::feff is out of the range
|
||||
var_dump($range->getAddressAtOffset(-257));
|
||||
|
||||
|
||||
$range2 = \IPLib\Factory::parseRangeString('::/0');
|
||||
|
||||
// This will print ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
|
||||
echo (string) $range2->getAddressAtOffset(-1);
|
||||
|
||||
// This will print ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
|
||||
echo (string) $range2->getAddressAtOffset('340282366920938463463374607431768211455');
|
||||
|
||||
// This will print ::1
|
||||
echo (string) $range2->getAddressAtOffset('-340282366920938463463374607431768211455');
|
||||
|
||||
```
|
||||
|
||||
### Parse an IP address range
|
||||
|
||||
To parse a subnet (CIDR) range:
|
||||
|
||||
```php
|
||||
$range = \IPLib\Range\Subnet::parseString('127.0.0.1/24');
|
||||
$range = \IPLib\Range\Subnet::parseString('::1/128');
|
||||
```
|
||||
|
||||
To parse a pattern (asterisk notation) range:
|
||||
|
||||
```php
|
||||
$range = \IPLib\Range\Pattern::parseString('127.0.0.*');
|
||||
$range = \IPLib\Range\Pattern::parseString('::*');
|
||||
```
|
||||
|
||||
To parse an address as a range:
|
||||
|
||||
```php
|
||||
$range = \IPLib\Range\Single::parseString('127.0.0.1');
|
||||
$range = \IPLib\Range\Single::parseString('::1');
|
||||
```
|
||||
|
||||
To parse a range in any format:
|
||||
|
||||
```php
|
||||
$range = \IPLib\Factory::parseRangeString('127.0.0.*');
|
||||
$range = \IPLib\Factory::parseRangeString('::1/128');
|
||||
$range = \IPLib\Factory::parseRangeString('::');
|
||||
```
|
||||
|
||||
### Retrieve a range from its boundaries
|
||||
|
||||
You can calculate the smallest range that comprises two addresses:
|
||||
|
||||
```php
|
||||
$range = \IPLib\Factory::getRangeFromBoundaries('192.168.0.1', '192.168.255.255');
|
||||
|
||||
// This will print 192.168.0.0/16
|
||||
echo (string) $range;
|
||||
```
|
||||
|
||||
You can also calculate a list of ranges that exactly describes all the addresses between two addresses:
|
||||
|
||||
```php
|
||||
$ranges = \IPLib\Factory::getRangesFromBoundaries('192.168.0.0', '192.168.0.5');
|
||||
|
||||
// This will print 192.168.0.0/30 192.168.0.4/31
|
||||
echo implode(' ', $ranges);
|
||||
```
|
||||
|
||||
### Retrieve a range that contains a set of IP addresses
|
||||
|
||||
You can use `IPLib\Factory::getRangeFromAddresses()` to retrieve the minimal IP range that contains all the provided IP addresses:
|
||||
|
||||
```php
|
||||
$range = \IPLib\Factory::getRangeFromAddresses(array(
|
||||
'1.2.2.225',
|
||||
'1.2.1.124',
|
||||
'1.2.3.237',
|
||||
));
|
||||
|
||||
// This will print 1.2.0.0/22
|
||||
echo (string) $range;
|
||||
```
|
||||
|
||||
### Retrieve the boundaries of a range
|
||||
|
||||
```php
|
||||
$range = \IPLib\Factory::parseRangeString('127.0.0.*');
|
||||
|
||||
// This will print 127.0.0.0
|
||||
echo (string) $range->getStartAddress();
|
||||
|
||||
// This will print 127.0.0.255
|
||||
echo (string) $range->getEndAddress();
|
||||
```
|
||||
|
||||
### Format addresses and ranges
|
||||
|
||||
Both IP addresses and ranges have a `toString` method that you can use to retrieve a textual representation:
|
||||
|
||||
```php
|
||||
// This will print 127.0.0.1
|
||||
echo \IPLib\Factory::parseAddressString('127.0.0.1')->toString();
|
||||
|
||||
// This will print 127.0.0.1
|
||||
echo \IPLib\Factory::parseAddressString('127.000.000.001')->toString();
|
||||
|
||||
// This will print ::1
|
||||
echo \IPLib\Factory::parseAddressString('::1')->toString();
|
||||
|
||||
// This will print ::1
|
||||
echo \IPLib\Factory::parseAddressString('0:0::1')->toString();
|
||||
|
||||
// This will print ::1/64
|
||||
echo \IPLib\Factory::parseRangeString('0:0::1/64')->toString();
|
||||
```
|
||||
|
||||
When working with IPv6, you may want the full (expanded) representation of the addresses. In this case, simply use a `true` parameter for the `toString` method:
|
||||
|
||||
```php
|
||||
// This will print 0000:0000:0000:0000:0000:0000:0000:0000
|
||||
echo \IPLib\Factory::parseAddressString('::')->toString(true);
|
||||
|
||||
// This will print 0000:0000:0000:0000:0000:0000:0000:0001
|
||||
echo \IPLib\Factory::parseAddressString('::1')->toString(true);
|
||||
|
||||
// This will print 0fff:0000:0000:0000:0000:0000:0000:0000
|
||||
echo \IPLib\Factory::parseAddressString('fff::')->toString(true);
|
||||
|
||||
// This will print 0000:0000:0000:0000:0000:0000:0000:0000
|
||||
echo \IPLib\Factory::parseAddressString('::0:0')->toString(true);
|
||||
|
||||
// This will print 0001:0002:0003:0004:0005:0006:0007:0008
|
||||
echo \IPLib\Factory::parseAddressString('1:2:3:4:5:6:7:8')->toString(true);
|
||||
|
||||
// This will print 0000:0000:0000:0000:0000:0000:0000:0001/64
|
||||
echo \IPLib\Factory::parseRangeString('0:0::1/64')->toString();
|
||||
```
|
||||
|
||||
You may also want a *long* representation for IPv4 addresses: here again you can use `true`as the parameter for the `toString` method:
|
||||
|
||||
```php
|
||||
// This will print 1.2.3.4
|
||||
echo \IPLib\Factory::parseAddressString('1.2.3.4')->toString();
|
||||
|
||||
// This will print 001.002.003.004
|
||||
echo \IPLib\Factory::parseAddressString('1.2.3.4')->toString(true);
|
||||
```
|
||||
|
||||
The address and range objects implements the `__toString()` method, which call the `toString()` method.
|
||||
So, if you want the string (short) representation of an object, you can do any of the following:
|
||||
|
||||
```php
|
||||
$address = \IPLib\Address\IPv6::parseString('::1');
|
||||
|
||||
// All these will print ::1
|
||||
echo $address->toString();
|
||||
echo $address->toString(false);
|
||||
echo (string) $address;
|
||||
```
|
||||
|
||||
### Check if an address is contained in a range
|
||||
|
||||
All the range types offer a `contains` method, and all the IP address types offer a `matches` method: you can call them to check if an address is contained in a range:
|
||||
|
||||
```php
|
||||
$address = \IPLib\Factory::parseAddressString('1:2:3:4:5:6:7:8');
|
||||
$range = \IPLib\Factory::parseRangeString('0:0::1/64');
|
||||
|
||||
$contained = $address->matches($range);
|
||||
// that's equivalent to
|
||||
$contained = $range->contains($address);
|
||||
```
|
||||
|
||||
Please remark that if the address is IPv4 and the range is IPv6 (or vice-versa), the result will always be `false`.
|
||||
|
||||
### Check if a range contains another range
|
||||
|
||||
All the range types offer a `containsRange` method: you can call them to check if an address range fully contains another range:
|
||||
|
||||
```php
|
||||
$range1 = \IPLib\Factory::parseRangeString('0:0::1/64');
|
||||
$range2 = \IPLib\Factory::parseRangeString('0:0::1/65');
|
||||
|
||||
$contained = $range1->containsRange($range2);
|
||||
```
|
||||
|
||||
### Getting the type of an IP address
|
||||
|
||||
If you want to know if an address is within a private network, or if it's a public IP, or whatever you want, you can use the `getRangeType` method:
|
||||
|
||||
```php
|
||||
$address = \IPLib\Factory::parseAddressString('::');
|
||||
|
||||
$type = $address->getRangeType();
|
||||
|
||||
$typeName = \IPLib\Range\Type::getName($type);
|
||||
```
|
||||
|
||||
The most notable values of the range type are:
|
||||
|
||||
- `\IPLib\Range\Type::T_UNSPECIFIED` if the address is all zeros (`0.0.0.0` or `::`)
|
||||
- `\IPLib\Range\Type::T_LOOPBACK` if the address is the localhost (usually `127.0.0.1` or `::1`)
|
||||
- `\IPLib\Range\Type::T_PRIVATENETWORK` if the address is in the local network (for instance `192.168.0.1` or `fc00::1`)
|
||||
- `\IPLib\Range\Type::T_PUBLIC` if the address is for public usage (for instance `104.25.25.33` or `2001:503:ba3e::2:30`)
|
||||
|
||||
### Getting the type of an IP address range
|
||||
|
||||
If you want to know the type of an address range, you can use the `getRangeType` method:
|
||||
|
||||
```php
|
||||
$range = \IPLib\Factory::parseRangeString('2000:0::1/64');
|
||||
|
||||
// $type will contain the value of \IPLib\Range\Type::T_PUBLIC
|
||||
$type = $range->getRangeType();
|
||||
|
||||
// This will print Public address
|
||||
echo \IPLib\Range\Type::getName($type);
|
||||
```
|
||||
|
||||
Please note that if a range spans across multiple range types, you'll get NULL as the range type:
|
||||
|
||||
```php
|
||||
$range = \IPLib\Factory::parseRangeString('::/127');
|
||||
|
||||
// $type will contain null
|
||||
$type = $range->getRangeType();
|
||||
|
||||
// This will print Unknown type
|
||||
echo \IPLib\Range\Type::getName($type);
|
||||
```
|
||||
|
||||
### Converting IP addresses
|
||||
|
||||
This library supports converting IPv4 to/from IPv6 addresses using the [6to4 notation](https://tools.ietf.org/html/rfc3056) or the [IPv4-mapped notation](https://tools.ietf.org/html/rfc4291#section-2.5.5.2):
|
||||
|
||||
```php
|
||||
$ipv4 = \IPLib\Factory::parseAddressString('1.2.3.4');
|
||||
|
||||
// 6to4 notation
|
||||
$ipv6 = $ipv4->toIPv6();
|
||||
|
||||
// This will print 2002:102:304::
|
||||
echo (string) $ipv6;
|
||||
|
||||
// This will print 1.2.3.4
|
||||
echo $ipv6->toIPv4();
|
||||
|
||||
// IPv4-mapped notation
|
||||
$ipv6_6to4 = $ipv4->toIPv6IPv4Mapped();
|
||||
|
||||
// This will print ::ffff:1.2.3.4
|
||||
echo (string) $ipv6_6to4;
|
||||
|
||||
// This will print 1.2.3.4
|
||||
echo $ipv6_6to4->toIPv4();
|
||||
```
|
||||
|
||||
### Converting IP ranges
|
||||
|
||||
This library supports IPv4/IPv6 ranges in pattern format (eg. `192.168.*.*`) and in CIDR/subnet format (eg. `192.168.0.0/16`), and it offers a way to convert between the two formats:
|
||||
|
||||
```php
|
||||
// This will print ::*:*:*:*
|
||||
echo \IPLib\Factory::parseRangeString('::/64')->asPattern()->toString();
|
||||
|
||||
// This will print 1:2::/96
|
||||
echo \IPLib\Factory::parseRangeString('1:2::*:*')->asSubnet()->toString();
|
||||
|
||||
// This will print 192.168.0.0/24
|
||||
echo \IPLib\Factory::parseRangeString('192.168.0.*')->asSubnet()->toString();
|
||||
|
||||
// This will print 10.*.*.*
|
||||
echo \IPLib\Factory::parseRangeString('10.0.0.0/8')->asPattern()->toString();
|
||||
```
|
||||
|
||||
Please remark that all the range types implement the `asPattern()` and `asSubnet()` methods.
|
||||
|
||||
### Splitting IP ranges
|
||||
|
||||
If you need to divide an IP address range into smaller ranges, you can use the `split` method.
|
||||
You can specify the length of the network prefix, as well as indicate whether you want to force the Subnet notation (by default, it is not).
|
||||
|
||||
For example:
|
||||
|
||||
```php
|
||||
$subnet = \IPLib\Factory::parseRangeString('192.168.112.203/24');
|
||||
$smallerSubnets = $subnet->split(25);
|
||||
print_r(array_map('strval', $smallerSubnets));
|
||||
/*
|
||||
* You'll have:
|
||||
* Array
|
||||
* (
|
||||
* [0] => 192.168.112.0/25
|
||||
* [1] => 192.168.112.128/25
|
||||
* )
|
||||
*/
|
||||
|
||||
$subnet = \IPLib\Factory::parseRangeString('192.168.*.*');
|
||||
$smallerSubnets = $subnet->split(24);
|
||||
print_r(array_map('strval', $smallerSubnets));
|
||||
/*
|
||||
* You'll have:
|
||||
* Array
|
||||
* (
|
||||
* [0] => 192.168.0.*
|
||||
* [1] => 192.168.1.*
|
||||
* [...]
|
||||
* [254] => 192.168.254.*
|
||||
* [255] => 192.168.255.*
|
||||
* )
|
||||
*/
|
||||
|
||||
$subnet = \IPLib\Factory::parseRangeString('192.168.*.*');
|
||||
$smallerSubnets = $subnet->split(24, true);
|
||||
print_r(array_map('strval', $smallerSubnets));
|
||||
/*
|
||||
* You'll have:
|
||||
* Array
|
||||
* (
|
||||
* [0] => 192.168.0.0/24
|
||||
* [1] => 192.168.1.0/24
|
||||
* [...]
|
||||
* [254] => 192.168.254.0/24
|
||||
* [255] => 192.168.255.0/24
|
||||
* )
|
||||
*/
|
||||
```
|
||||
|
||||
### Getting the subnet mask for IPv4 ranges
|
||||
|
||||
You can use the `getSubnetMask()` to get the subnet mask for IPv4 ranges:
|
||||
|
||||
```php
|
||||
// This will print 255.255.255.0
|
||||
echo \IPLib\Factory::parseRangeString('192.168.0.*')->getSubnetMask()->toString();
|
||||
|
||||
// This will print 255.255.255.252
|
||||
echo \IPLib\Factory::parseRangeString('192.168.0.12/30')->getSubnetMask()->toString();
|
||||
```
|
||||
|
||||
### Getting the range size
|
||||
|
||||
You can use the `getSize()` to get the count of addresses this IP range contains:
|
||||
|
||||
```php
|
||||
// This will print 256
|
||||
echo \IPLib\Factory::parseRangeString('192.168.0.*')->getSize();
|
||||
|
||||
// This will print 4
|
||||
echo \IPLib\Factory::parseRangeString('192.168.0.12/30')->getSize();
|
||||
|
||||
// This will print 1
|
||||
echo \IPLib\Factory::parseRangeString('192.168.0.1')->getSize();
|
||||
```
|
||||
|
||||
Please note that if the number of IP addresses contained in the range is greater than the maximum integer supported by the operating system (2,147,483,647 for 32-bit systems, 9,223,372,036,854,775,807 for 64-bit systems), the `getSize()` method will return a `float` (which may be not precise).
|
||||
|
||||
If instead you want the exact number of IP addresses, you can use the `getExactSize()` method, which will return a string containing the number of IP addresses in decimal format in case of such big numbers.
|
||||
|
||||
```php
|
||||
// This will print:
|
||||
// int(1)
|
||||
var_dump(\IPLib\Factory::parseRangeString('0.0.0.0/32')->getExactSize());
|
||||
|
||||
// On 32-bit systems, this will print
|
||||
// string(10) "2147483648"
|
||||
// On 64-bit systems, this will print
|
||||
// int(2147483648)
|
||||
var_dump(\IPLib\Factory::parseRangeString('0.0.0.0/1')->getExactSize());
|
||||
|
||||
// This will print:
|
||||
// int(1073741824)
|
||||
var_dump(\IPLib\Factory::parseRangeString('::/98')->getExactSize());
|
||||
|
||||
// On 32-bit systems, this will print
|
||||
// string(10) "2147483648"
|
||||
// On 64-bit systems, this will print
|
||||
// int(2147483648)
|
||||
var_dump(\IPLib\Factory::parseRangeString('::/97')->getExactSize());
|
||||
|
||||
// On 32-bit and 64-bit systems, this will print
|
||||
// string(39) "170141183460469231731687303715884105728"
|
||||
var_dump(\IPLib\Factory::parseRangeString('::/1')->getExactSize());
|
||||
```
|
||||
|
||||
### Getting the reverse DNS lookup address
|
||||
|
||||
To perform reverse DNS queries, you need to use a special format of the IP addresses.
|
||||
|
||||
You can use the `getReverseDNSLookupName()` method of the IP address instances to retrieve it easily:
|
||||
|
||||
```php
|
||||
$ipv4 = \IPLib\Factory::parseAddressString('1.2.3.255');
|
||||
$ipv6 = \IPLib\Factory::parseAddressString('1234:abcd::cafe:babe');
|
||||
|
||||
// This will print 255.3.2.1.in-addr.arpa
|
||||
echo $ipv4->getReverseDNSLookupName();
|
||||
|
||||
// This will print e.b.a.b.e.f.a.c.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.c.b.a.4.3.2.1.ip6.arpa
|
||||
echo $ipv6->getReverseDNSLookupName();
|
||||
```
|
||||
|
||||
To parse addresses in reverse DNS lookup format you can use the `IPLib\ParseStringFlag::ADDRESS_MAYBE_RDNS` flag when parsing a string:
|
||||
|
||||
```php
|
||||
$ipv4 = \IPLib\Factory::parseAddressString('255.3.2.1.in-addr.arpa', \IPLib\ParseStringFlag::ADDRESS_MAYBE_RDNS);
|
||||
$ipv6 = \IPLib\Factory::parseAddressString('e.b.a.b.e.f.a.c.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.c.b.a.4.3.2.1.ip6.arpa', \IPLib\ParseStringFlag::ADDRESS_MAYBE_RDNS);
|
||||
|
||||
// This will print 1.2.3.255
|
||||
echo $ipv4->toString();
|
||||
|
||||
// This will print 1234:abcd::cafe:babe
|
||||
echo $ipv6->toString();
|
||||
```
|
||||
|
||||
You can also use `getReverseDNSLookupName()` for IP ranges.
|
||||
In this case, the result is an array of strings:
|
||||
|
||||
```php
|
||||
$range = \IPLib\Factory::parseRangeString('10.155.16.0/22');
|
||||
|
||||
/*
|
||||
* This will print:
|
||||
* array (
|
||||
* 0 => '16.155.10.in-addr.arpa',
|
||||
* 1 => '17.155.10.in-addr.arpa',
|
||||
* 2 => '18.155.10.in-addr.arpa',
|
||||
* 3 => '19.155.10.in-addr.arpa',
|
||||
* )
|
||||
*/
|
||||
var_export($range->getReverseDNSLookupName());
|
||||
```
|
||||
|
||||
### Using a database
|
||||
|
||||
This package offers a great feature: you can store address ranges in a database table, and check if an address is contained in one of the saved ranges with a simple query.
|
||||
|
||||
To save a range, you need to store the address type (for IPv4 it's `4`, for IPv6 it's `6`), as well as two values representing the start and the end of the range.
|
||||
These methods are:
|
||||
```php
|
||||
$range->getAddressType();
|
||||
$range->getComparableStartString();
|
||||
$range->getComparableEndString();
|
||||
```
|
||||
|
||||
Let's assume that you saved the type in a field called `addressType`, and the range boundaries in two fields called `rangeFrom` and `rangeTo`.
|
||||
|
||||
When you want to check if an address is within a stored range, simply use the `getComparableString` method of the address and check if it's between the fields `rangeFrom` and `rangeTo`, and check if the stored `addressType` is the same as the one of the address instance you want to check.
|
||||
|
||||
Here's a sample code:
|
||||
|
||||
```php
|
||||
/*
|
||||
* Let's assume that:
|
||||
* - $pdo is a PDO instance
|
||||
* - $range is a range object
|
||||
* - $address is an address object
|
||||
*/
|
||||
|
||||
// Save the $range object
|
||||
$insertQuery = $pdo->prepare('
|
||||
insert into ranges (addressType, rangeFrom, rangeTo)
|
||||
values (:addressType, :rangeFrom, :rangeTo)
|
||||
');
|
||||
|
||||
$insertQuery->execute(array(
|
||||
':addressType' => $range->getAddressType(),
|
||||
':rangeFrom' => $range->getComparableStartString(),
|
||||
':rangeTo' => $range->getComparableEndString(),
|
||||
));
|
||||
|
||||
// Retrieve the saved ranges where an address $address falls:
|
||||
$searchQuery = $pdo->prepare('
|
||||
select * from ranges
|
||||
where addressType = :addressType
|
||||
and :address between rangeFrom and rangeTo
|
||||
');
|
||||
|
||||
$searchQuery->execute(array(
|
||||
':addressType' => $address->getAddressType(),
|
||||
':address' => $address->getComparableString(),
|
||||
));
|
||||
|
||||
$rows = $searchQuery->fetchAll();
|
||||
$searchQuery->closeCursor();
|
||||
```
|
||||
|
||||
## Handling non-standard address and range strings
|
||||
|
||||
### Accepting ports
|
||||
|
||||
If you want to accept addresses that may include ports, you can specify the `IPLib\ParseStringFlag::MAY_INCLUDE_PORT` flag:
|
||||
|
||||
```php
|
||||
use IPLib\Factory;
|
||||
use IPLib\ParseStringFlag;
|
||||
|
||||
require_once __DIR__ . '/../ip-lib.php';
|
||||
|
||||
// These will print NULL
|
||||
var_export(Factory::parseAddressString('127.0.0.1:80'));
|
||||
var_export(Factory::parseAddressString('[::]:80'));
|
||||
|
||||
// This will print 127.0.0.1
|
||||
echo (string) Factory::parseAddressString('127.0.0.1:80', ParseStringFlag::MAY_INCLUDE_PORT);
|
||||
// This will print ::
|
||||
echo (string) Factory::parseAddressString('[::]:80', ParseStringFlag::MAY_INCLUDE_PORT);
|
||||
```
|
||||
|
||||
### Accepting IPv6 zone IDs
|
||||
|
||||
If you want to accept IPv6 addresses that may include a zone ID, you can specify the `IPLib\ParseStringFlag::MAY_INCLUDE_ZONEID` flag:
|
||||
|
||||
```php
|
||||
use IPLib\Factory;
|
||||
use IPLib\ParseStringFlag;
|
||||
|
||||
// This will print NULL
|
||||
var_export(Factory::parseAddressString('::%11'));
|
||||
|
||||
// This will print ::
|
||||
echo (string) Factory::parseAddressString('::%11', ParseStringFlag::MAY_INCLUDE_ZONEID);
|
||||
```
|
||||
|
||||
### Accepting non-decimal IPv4 addresses
|
||||
|
||||
IPv4 addresses are usually expressed in decimal notation, for example as `192.168.0.1`.
|
||||
|
||||
By the way, the GNU (used in many Linux distros), BSD (used in Mac) and Windows implementations of `inet_aton` and `inet_addr` accept IPv4 addresses with numbers in octal and/or hexadecimal format.
|
||||
Please remark that this does not apply to the `inet_pton` and `ip2long` functions, as well as to the Musl implementation (used in Alpine Linux) of `inet_aton` and `inet_addr`.
|
||||
|
||||
So, for example, these addresses are all equivalent to `192.168.0.1`:
|
||||
|
||||
- `0xC0.0xA8.0x0.0x01` (only hexadecimal)
|
||||
- `0300.0250.00.01` (only octal)
|
||||
- `192.0250.0.0x01` (decimal, octal and hexadecimal numbers)
|
||||
|
||||
(try it: if you browse to [`http://0177.0.0.0x1`](http://0177.0.0.0x1), your browser will try to browse `http://127.0.0.1`).
|
||||
|
||||
If you want to accept this non-decimal syntax, you may use the `IPLib\ParseStringFlag::IPV4_MAYBE_NON_DECIMAL` flag:
|
||||
|
||||
```php
|
||||
use IPLib\Factory;
|
||||
use IPLib\ParseStringFlag;
|
||||
|
||||
// This will print NULL
|
||||
var_export(Factory::parseAddressString('0177.0.0.0x1'));
|
||||
|
||||
// This will print 127.0.0.1
|
||||
var_export((string) Factory::parseAddressString('0177.0.0.0x1', ParseStringFlag::IPV4_MAYBE_NON_DECIMAL));
|
||||
|
||||
// This will print NULL
|
||||
var_export(Factory::parseRangeString('0177.0.0.0x1/32'));
|
||||
|
||||
// This will print 127.0.0.1/32
|
||||
var_export((string) Factory::parseRangeString('0177.0.0.0x1/32', ParseStringFlag::IPV4_MAYBE_NON_DECIMAL));
|
||||
```
|
||||
|
||||
Please be aware that the `IPV4_MAYBE_NON_DECIMAL` flag may also affect parsing decimal numbers:
|
||||
|
||||
```php
|
||||
use IPLib\Factory;
|
||||
use IPLib\ParseStringFlag;
|
||||
|
||||
// This will print 127.0.0.10 since the last digit is assumed to be decimal
|
||||
var_export((string) Factory::parseAddressString('127.0.0.010'));
|
||||
|
||||
// This will print 127.0.0.8 since the last digit is assumed to be octal
|
||||
var_export((string) Factory::parseAddressString('127.0.0.010', ParseStringFlag::IPV4_MAYBE_NON_DECIMAL));
|
||||
```
|
||||
|
||||
### Accepting IPv4 addresses in not-quad-dotted notation
|
||||
|
||||
IPv4 addresses are usually expressed with 4 numbers, for example as `192.168.0.1`.
|
||||
|
||||
By the way, the GNU (used in many Linux distros), BSD (used in Mac) and Windows implementations of `inet_aton` and `inet_addr` [accept IPv4 addresses with 1 to 4 numbers](https://man7.org/linux/man-pages/man3/inet_addr.3.html#DESCRIPTION).
|
||||
|
||||
Please remark that this does not apply to the `inet_pton` and `ip2long` functions, as well as to the Musl implementation (used in Alpine Linux) of `inet_aton` and `inet_addr`.
|
||||
|
||||
If you want to accept this non-decimal syntax, you may use the `IPLib\ParseStringFlag::IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED` flag:
|
||||
|
||||
```php
|
||||
use IPLib\Factory;
|
||||
use IPLib\ParseStringFlag;
|
||||
|
||||
// This will print NULL
|
||||
var_export(Factory::parseAddressString('1.2.500'));
|
||||
|
||||
// This will print 0.0.0.0
|
||||
var_export((string) Factory::parseAddressString('0', ParseStringFlag::IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED));
|
||||
|
||||
// This will print 0.0.0.1
|
||||
var_export((string) Factory::parseAddressString('1', ParseStringFlag::IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED));
|
||||
|
||||
// This will print 0.0.1.244
|
||||
var_export((string) Factory::parseAddressString('0.0.500', ParseStringFlag::IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED));
|
||||
|
||||
// This will print 255.255.255.255
|
||||
var_export((string) Factory::parseAddressString('4294967295', ParseStringFlag::IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED));
|
||||
```
|
||||
|
||||
### Accepting compact IPv4 subnet notation
|
||||
|
||||
Even if there isn't an RFC that describe it, IPv4 subnet notation may also be written in a compact form, omitting extra digits (for example, `127.0.0.0/24` may be written as `127/24`).
|
||||
If you want to accept such format, you can specify the `IPLib\ParseStringFlag::IPV4SUBNET_MAYBE_COMPACT` flag:
|
||||
|
||||
```php
|
||||
use IPLib\Factory;
|
||||
use IPLib\ParseStringFlag;
|
||||
|
||||
// This will print NULL
|
||||
var_export(Factory::parseRangeString('127/24'));
|
||||
|
||||
// This will print 127.0.0.0/24
|
||||
echo (string) Factory::parseRangeString('127/24', ParseStringFlag::IPV4SUBNET_MAYBE_COMPACT);
|
||||
```
|
||||
|
||||
### Combining multiple flags
|
||||
|
||||
Of course, you may use more than one `IPLib\ParseStringFlag` flag at once:
|
||||
|
||||
```php
|
||||
use IPLib\Factory;
|
||||
use IPLib\ParseStringFlag;
|
||||
|
||||
// This will print 127.0.0.255
|
||||
var_export((string) Factory::parseAddressString('127.0.0.0xff:80', ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::IPV4_MAYBE_NON_DECIMAL));
|
||||
|
||||
// This will print ::
|
||||
var_export((string) Factory::parseAddressString('[::%11]:80', ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::MAY_INCLUDE_ZONEID));
|
||||
```
|
||||
|
||||
## Gitpod Environment Variables
|
||||
|
||||
The following features can be enabled through environment variables that have been set in your [Gitpod preferences](https://gitpod.io/variables).:
|
||||
|
||||
\* _Please note that storing sensitive data in environment variables is not ultimately secure but should be OK for most development situations._
|
||||
- ### Sign Git commits with a GPG key
|
||||
- `GPG_KEY_ID` (required)
|
||||
- The ID of the GPG key you want to use to sign your git commits
|
||||
- `GPG_KEY` (required)
|
||||
- Base64 encoded private GPG key that corresponds to your `GPG_KEY_ID`
|
||||
- `GPG_MATCH_GIT_TO_EMAIL` (optional)
|
||||
- Sets your git user.email in `~/.gitconfig` to the value provided
|
||||
- `GPG_AUTO_ULTIMATE_TRUST` (optional)
|
||||
- If the value is set to `yes` or `YES` then your `GPG_KEY` will be automatically ultimately trusted
|
||||
- ### Activate an Intelliphense License Key
|
||||
- `INTELEPHENSE_LICENSEKEY`
|
||||
- Creates `~/intelephense/licence.txt` and will contain the value provided
|
||||
- This will activate [Intelliphense](https://intelephense.com/) for you each time the workspace is created or restarted
|
||||
|
||||
## Do you really want to say thank you?
|
||||
|
||||
You can offer me a [monthly coffee](https://github.com/sponsors/mlocati) or a [one-time coffee](https://paypal.me/mlocati) :wink:
|
||||
Vendored
Executable
+60
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"name": "mlocati/ip-lib",
|
||||
"description": "Handle IPv4, IPv6 addresses and ranges",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/mlocati/ip-lib",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michele Locati",
|
||||
"homepage": "https://github.com/mlocati",
|
||||
"email": "mlocati@gmail.com",
|
||||
"role": "Author"
|
||||
}
|
||||
],
|
||||
"keywords": [
|
||||
"ip",
|
||||
"ipv4",
|
||||
"ipv6",
|
||||
"range",
|
||||
"network",
|
||||
"networking",
|
||||
"address",
|
||||
"addresses",
|
||||
"subnet",
|
||||
"matching",
|
||||
"managing",
|
||||
"manage"
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"IPLib\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"IPLib\\Test\\": "test/tests/",
|
||||
"IPLib\\Test\\Helpers\\": "test/helpers/"
|
||||
}
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-pdo_sqlite": "*",
|
||||
"phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5 || ^8.5 || ^9.5"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "phpunit"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/mlocati"
|
||||
},
|
||||
{
|
||||
"type": "other",
|
||||
"url": "https://paypal.me/mlocati"
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
Executable
+13
@@ -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;
|
||||
}
|
||||
}
|
||||
);
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
Executable
+342
@@ -0,0 +1,342 @@
|
||||
<?php
|
||||
|
||||
namespace IPLib;
|
||||
|
||||
use IPLib\Address\AddressInterface;
|
||||
use IPLib\Range\Subnet;
|
||||
use IPLib\Service\RangesFromBoundaryCalculator;
|
||||
|
||||
/**
|
||||
* Factory methods to build class instances.
|
||||
*/
|
||||
class Factory
|
||||
{
|
||||
/**
|
||||
* @deprecated since 1.17.0: use the parseAddressString() 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
|
||||
* - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
|
||||
*
|
||||
* @param string|mixed $address
|
||||
* @param bool $mayIncludePort
|
||||
* @param bool $mayIncludeZoneID
|
||||
* @param bool $supportNonDecimalIPv4
|
||||
*
|
||||
* @return \IPLib\Address\AddressInterface|null
|
||||
*
|
||||
* @see \IPLib\Factory::parseAddressString()
|
||||
* @since 1.1.0 added the $mayIncludePort argument
|
||||
* @since 1.3.0 added the $mayIncludeZoneID argument
|
||||
* @since 1.10.0 added the $supportNonDecimalIPv4 argument
|
||||
*/
|
||||
public static function addressFromString($address, $mayIncludePort = true, $mayIncludeZoneID = true, $supportNonDecimalIPv4 = false)
|
||||
{
|
||||
return static::parseAddressString($address, 0 + ($mayIncludePort ? ParseStringFlag::MAY_INCLUDE_PORT : 0) + ($mayIncludeZoneID ? ParseStringFlag::MAY_INCLUDE_ZONEID : 0) + ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an IP address string.
|
||||
*
|
||||
* @param string|mixed $address the address to parse
|
||||
* @param int $flags A combination or zero or more flags
|
||||
*
|
||||
* @return \IPLib\Address\AddressInterface|null
|
||||
*
|
||||
* @see \IPLib\ParseStringFlag
|
||||
* @since 1.17.0
|
||||
*/
|
||||
public static function parseAddressString($address, $flags = 0)
|
||||
{
|
||||
if (($result = Address\IPv4::parseString($address, $flags)) !== null) {
|
||||
return $result;
|
||||
}
|
||||
if (($result = Address\IPv6::parseString($address, $flags)) !== null) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a byte array to an address instance.
|
||||
*
|
||||
* @param array<int|mixed> $bytes
|
||||
*
|
||||
* @return \IPLib\Address\AddressInterface|null
|
||||
*/
|
||||
public static function addressFromBytes(array $bytes)
|
||||
{
|
||||
if (($result = Address\IPv4::fromBytes($bytes)) !== null) {
|
||||
return $result;
|
||||
}
|
||||
if (($result = Address\IPv6::fromBytes($bytes)) !== null) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.17.0: use the parseRangeString() method instead.
|
||||
* For upgrading:
|
||||
* - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
|
||||
*
|
||||
* @param string|mixed $range
|
||||
* @param bool $supportNonDecimalIPv4
|
||||
*
|
||||
* @return \IPLib\Range\RangeInterface|null
|
||||
*
|
||||
* @see \IPLib\Factory::parseRangeString()
|
||||
* @since 1.10.0 added the $supportNonDecimalIPv4 argument
|
||||
*/
|
||||
public static function rangeFromString($range, $supportNonDecimalIPv4 = false)
|
||||
{
|
||||
return static::parseRangeString($range, $supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an IP range string.
|
||||
*
|
||||
* @param string|mixed $range
|
||||
* @param int $flags A combination or zero or more flags
|
||||
*
|
||||
* @return \IPLib\Range\RangeInterface|null
|
||||
*
|
||||
* @see \IPLib\ParseStringFlag
|
||||
* @since 1.17.0
|
||||
*/
|
||||
public static function parseRangeString($range, $flags = 0)
|
||||
{
|
||||
$result = Range\Subnet::parseString($range, $flags);
|
||||
if ($result === null) {
|
||||
$result = Range\Pattern::parseString($range, $flags);
|
||||
}
|
||||
if ($result === null) {
|
||||
$result = Range\Single::parseString($range, $flags);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.17.0: use the getRangeFromBoundaries() method instead.
|
||||
* For upgrading:
|
||||
* - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
|
||||
*
|
||||
* @param string|\IPLib\Address\AddressInterface|mixed $from
|
||||
* @param string|\IPLib\Address\AddressInterface|mixed $to
|
||||
* @param bool $supportNonDecimalIPv4
|
||||
*
|
||||
* @return \IPLib\Range\RangeInterface|null
|
||||
*
|
||||
* @see \IPLib\Factory::getRangeFromBoundaries()
|
||||
* @since 1.2.0
|
||||
* @since 1.10.0 added the $supportNonDecimalIPv4 argument
|
||||
*/
|
||||
public static function rangeFromBoundaries($from, $to, $supportNonDecimalIPv4 = false)
|
||||
{
|
||||
return static::getRangeFromBoundaries($from, $to, ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::MAY_INCLUDE_ZONEID | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the smallest address range that comprises two addresses.
|
||||
*
|
||||
* @param string|\IPLib\Address\AddressInterface|mixed $from
|
||||
* @param string|\IPLib\Address\AddressInterface|mixed $to
|
||||
* @param int $flags A combination or zero or more flags
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @see \IPLib\ParseStringFlag
|
||||
* @since 1.17.0
|
||||
*/
|
||||
public static function getRangeFromBoundaries($from, $to, $flags = 0)
|
||||
{
|
||||
list($from, $to) = self::parseBoundaries($from, $to, $flags);
|
||||
|
||||
return $from === false || $to === false ? null : static::rangeFromBoundaryAddresses($from, $to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the minimal range that contains all the specified addresses.
|
||||
*
|
||||
* @param array<non-empty-string|\IPLib\Address\AddressInterface|mixed> $addresses
|
||||
* @param int $flags
|
||||
*
|
||||
* @return \IPLib\Range\RangeInterface|null Returns NULL if $addresses is empty, if it contains invalid addresses, or if the addresses aren't compatible (for example, both IPv4 and IPv6 addresses)
|
||||
*
|
||||
* @since 1.22.0
|
||||
*/
|
||||
public static function getRangeFromAddresses(array $addresses, $flags = 0)
|
||||
{
|
||||
$min = null;
|
||||
$max = null;
|
||||
$numberOfBits = null;
|
||||
foreach ($addresses as $address) {
|
||||
if (!$address instanceof AddressInterface) {
|
||||
$address = Factory::parseAddressString($address, $flags);
|
||||
if ($address === null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if ($numberOfBits === null) {
|
||||
$min = $address;
|
||||
$max = $address;
|
||||
$numberOfBits = $address->getNumberOfBits();
|
||||
} elseif ($numberOfBits !== $address->getNumberOfBits()) {
|
||||
return null;
|
||||
} else {
|
||||
/** @var AddressInterface $min */
|
||||
/** @var AddressInterface $max */
|
||||
$comparable = $address->getComparableString();
|
||||
if ($min->getComparableString() > $comparable) {
|
||||
$min = $address;
|
||||
}
|
||||
if ($max->getComparableString() < $comparable) {
|
||||
$max = $address;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($numberOfBits === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return static::rangeFromBoundaryAddresses($min, $max);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.17.0: use the getRangesFromBoundaries() method instead.
|
||||
* For upgrading:
|
||||
* - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
|
||||
*
|
||||
* @param string|\IPLib\Address\AddressInterface|mixed $from
|
||||
* @param string|\IPLib\Address\AddressInterface|mixed $to
|
||||
* @param bool $supportNonDecimalIPv4
|
||||
*
|
||||
* @return \IPLib\Range\Subnet[]|null
|
||||
*
|
||||
* @see \IPLib\Factory::getRangesFromBoundaries()
|
||||
* @since 1.14.0
|
||||
*/
|
||||
public static function rangesFromBoundaries($from, $to, $supportNonDecimalIPv4 = false)
|
||||
{
|
||||
return static::getRangesFromBoundaries($from, $to, ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::MAY_INCLUDE_ZONEID | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a list of Range instances that exactly describes all the addresses between the two provided addresses.
|
||||
*
|
||||
* @param string|\IPLib\Address\AddressInterface|mixed $from
|
||||
* @param string|\IPLib\Address\AddressInterface|mixed $to
|
||||
* @param int $flags A combination or zero or more flags
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @see \IPLib\ParseStringFlag
|
||||
* @since 1.17.0
|
||||
*/
|
||||
public static function getRangesFromBoundaries($from, $to, $flags = 0)
|
||||
{
|
||||
list($from, $to) = self::parseBoundaries($from, $to, $flags);
|
||||
if ($from === false || $to === false || ($from === null && $to === null)) {
|
||||
return null;
|
||||
}
|
||||
if ($from === null || $to === null) {
|
||||
$address = $from ? $from : $to;
|
||||
/** @var AddressInterface $address */
|
||||
|
||||
return array(new Subnet($address, $address, $address->getNumberOfBits()));
|
||||
}
|
||||
$numberOfBits = $from->getNumberOfBits();
|
||||
if ($to->getNumberOfBits() !== $numberOfBits) {
|
||||
return null;
|
||||
}
|
||||
$calculator = new RangesFromBoundaryCalculator($numberOfBits);
|
||||
|
||||
return $calculator->getRanges($from, $to);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \IPLib\Address\AddressInterface|null $from
|
||||
* @param \IPLib\Address\AddressInterface|null $to
|
||||
*
|
||||
* @return \IPLib\Range\RangeInterface|null
|
||||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
protected static function rangeFromBoundaryAddresses($from = null, $to = null)
|
||||
{
|
||||
if (!$from instanceof AddressInterface && !$to instanceof AddressInterface) {
|
||||
$result = null;
|
||||
} elseif (!$to instanceof AddressInterface) {
|
||||
$result = Range\Single::fromAddress($from);
|
||||
} elseif (!$from instanceof AddressInterface) {
|
||||
$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::parseRangeString($from->toString() . '/' . (string) $sameBits);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|\IPLib\Address\AddressInterface|mixed $from
|
||||
* @param string|\IPLib\Address\AddressInterface|mixed $to
|
||||
* @param int $flags
|
||||
*
|
||||
* @return array{\IPLib\Address\AddressInterface|false|null, \IPLib\Address\AddressInterface|false|null}
|
||||
*/
|
||||
private static function parseBoundaries($from, $to, $flags = 0)
|
||||
{
|
||||
$result = array();
|
||||
foreach (array('from', 'to') as $param) {
|
||||
$value = $$param;
|
||||
if (!($value instanceof AddressInterface)) {
|
||||
$value = is_object($value) && method_exists($value, '__toString') || is_scalar($value) ? (string) $value : '';
|
||||
if ($value === '') {
|
||||
$value = null;
|
||||
} else {
|
||||
$value = static::parseAddressString($value, $flags);
|
||||
if ($value === null) {
|
||||
$value = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
$result[] = $value;
|
||||
}
|
||||
/** @var array{\IPLib\Address\AddressInterface|false|null, \IPLib\Address\AddressInterface|false|null} $result */
|
||||
if ($result[0] && $result[1] && strcmp($result[0]->getComparableString(), $result[1]->getComparableString()) > 0) {
|
||||
$result = array($result[1], $result[0]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
wp-content/plugins/all-in-one-wp-security-and-firewall/vendor/mlocati/ip-lib/src/ParseStringFlag.php
Vendored
Executable
+79
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace IPLib;
|
||||
|
||||
/**
|
||||
* Flags for the parseString() methods.
|
||||
*
|
||||
* @since 1.17.0
|
||||
*/
|
||||
class ParseStringFlag
|
||||
{
|
||||
/**
|
||||
* Use this flag if the input string may include the port.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const MAY_INCLUDE_PORT = 1;
|
||||
|
||||
/**
|
||||
* Use this flag if the input string may include a zone ID.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const MAY_INCLUDE_ZONEID = 2;
|
||||
|
||||
/**
|
||||
* Use this flag if IPv4 addresses may be in decimal/octal/hexadecimal format.
|
||||
* This notation is accepted by the implementation of inet_aton and inet_addr of the libc implementation of GNU, Windows and Mac (but not Musl), but not by inet_pton and ip2long.
|
||||
*
|
||||
* @var int
|
||||
*
|
||||
* @example 1.08.0x10.0 => 5.0.0.1
|
||||
* @example 5.256 => 5.0.1.0
|
||||
* @example 5.0.256 => 5.0.1.0
|
||||
* @example 123456789 => 7.91.205.21
|
||||
*/
|
||||
const IPV4_MAYBE_NON_DECIMAL = 4;
|
||||
|
||||
/**
|
||||
* Use this flag if IPv4 subnet ranges may be in compact form.
|
||||
*
|
||||
* @example 127/24 => 127.0.0.0/24
|
||||
* @example 10/8 => 10.0.0.0/8
|
||||
* @example 10/24 => 10.0.0.0/24
|
||||
* @example 10.10.10/24 => 10.10.10.0/24
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const IPV4SUBNET_MAYBE_COMPACT = 8;
|
||||
|
||||
/**
|
||||
* Use this flag if IPv4 addresses may be in non quad-dotted notation.
|
||||
* This notation is accepted by the implementation of inet_aton and inet_addr of the libc implementation of GNU, Windows and Mac (but not Musl), but not by inet_pton and ip2long.
|
||||
*
|
||||
* @var int
|
||||
*
|
||||
* @example 5.1 => 5.0.0.1
|
||||
* @example 5.256 => 5.0.1.0
|
||||
* @example 5.0.256 => 5.0.1.0
|
||||
* @example 123456789 => 7.91.205.21
|
||||
*
|
||||
* @see https://man7.org/linux/man-pages/man3/inet_addr.3.html#DESCRIPTION
|
||||
* @see https://www.freebsd.org/cgi/man.cgi?query=inet_net&sektion=3&apropos=0&manpath=FreeBSD+12.2-RELEASE+and+Ports#end
|
||||
* @see http://git.musl-libc.org/cgit/musl/tree/src/network/inet_aton.c?h=v1.2.2
|
||||
*/
|
||||
const IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED = 16;
|
||||
|
||||
/**
|
||||
* Use this flag if you want to accept parsing IPv4/IPv6 addresses in Reverse DNS Lookup Address format.
|
||||
*
|
||||
* @var int
|
||||
*
|
||||
* @since 1.18.0
|
||||
*
|
||||
* @example 140.13.12.10.in-addr.arpa => 10.12.13.140
|
||||
* @example b.a.9.8.7.6.5.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.0.0.0.0.1.2.3.4.ip6.arpa => 4321:0:1:2:3:4:567:89ab
|
||||
*/
|
||||
const ADDRESS_MAYBE_RDNS = 32;
|
||||
}
|
||||
Vendored
Executable
+188
@@ -0,0 +1,188 @@
|
||||
<?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;
|
||||
use IPLib\Service\BinaryMath;
|
||||
use OutOfBoundsException;
|
||||
|
||||
/**
|
||||
* Base class for range classes.
|
||||
*/
|
||||
abstract class AbstractRange implements RangeInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \IPLib\Range\RangeInterface::getRangeType()
|
||||
*/
|
||||
public function getRangeType()
|
||||
{
|
||||
/** @var \IPLib\Range\Pattern|\IPLib\Range\Subnet $this */
|
||||
// @phpstan-ignore varTag.nativeType
|
||||
if ($this->rangeType === null) {
|
||||
$addressType = $this->getAddressType();
|
||||
if ($addressType === AddressType::T_IPv6 && Subnet::get6to4()->containsRange($this)) {
|
||||
$fromAddress = $this->fromAddress;
|
||||
/** @var IPv6 $fromAddress */
|
||||
$toAddress = $this->toAddress;
|
||||
/** @var IPv6 $toAddress */
|
||||
$range = Factory::getRangeFromBoundaries($fromAddress->toIPv4(), $toAddress->toIPv4());
|
||||
/** @var RangeInterface $range */
|
||||
$this->rangeType = $range->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;
|
||||
}
|
||||
$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::getAddressAtOffset()
|
||||
*/
|
||||
public function getAddressAtOffset($n)
|
||||
{
|
||||
if (is_int($n)) {
|
||||
$positive = $n >= 0;
|
||||
} elseif (($s = BinaryMath::getInstance()->normalizeIntegerString($n)) !== '') {
|
||||
$n = $s;
|
||||
$positive = $n[0] !== '-';
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
if ($positive) {
|
||||
$start = Factory::parseAddressString($this->getComparableStartString());
|
||||
/** @var \IPLib\Address\AddressInterface $start */
|
||||
$address = $start->getAddressAtOffset($n);
|
||||
} else {
|
||||
$end = Factory::parseAddressString($this->getComparableEndString());
|
||||
/** @var \IPLib\Address\AddressInterface $end */
|
||||
$nPlus1 = is_int($n) ? $n + 1 : BinaryMath::getInstance()->add1ToIntegerString($n);
|
||||
$address = $end->getAddressAtOffset($nPlus1);
|
||||
}
|
||||
|
||||
if ($address === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->contains($address) ? $address : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \IPLib\Range\RangeInterface::contains()
|
||||
*/
|
||||
public function contains(AddressInterface $address)
|
||||
{
|
||||
$result = false;
|
||||
if ($address->getAddressType() === $this->getAddressType()) {
|
||||
$cmp = $address->getComparableString();
|
||||
$from = $this->getComparableStartString();
|
||||
if ($cmp >= $from) {
|
||||
$to = $this->getComparableEndString();
|
||||
if ($cmp <= $to) {
|
||||
$result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \IPLib\Range\RangeInterface::containsRange()
|
||||
*/
|
||||
public function containsRange(RangeInterface $range)
|
||||
{
|
||||
$result = false;
|
||||
if ($range->getAddressType() === $this->getAddressType()) {
|
||||
$myStart = $this->getComparableStartString();
|
||||
$itsStart = $range->getComparableStartString();
|
||||
if ($itsStart >= $myStart) {
|
||||
$myEnd = $this->getComparableEndString();
|
||||
$itsEnd = $range->getComparableEndString();
|
||||
if ($itsEnd <= $myEnd) {
|
||||
$result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \IPLib\Range\RangeInterface::split()
|
||||
*/
|
||||
public function split($networkPrefix, $forceSubnet = false)
|
||||
{
|
||||
$networkPrefix = (int) $networkPrefix;
|
||||
$myNetworkPrefix = $this->getNetworkPrefix();
|
||||
if ($networkPrefix === $myNetworkPrefix) {
|
||||
return array(
|
||||
$forceSubnet ? $this->asSubnet() : $this,
|
||||
);
|
||||
}
|
||||
if ($networkPrefix < $myNetworkPrefix) {
|
||||
throw new OutOfBoundsException("The value of the \$networkPrefix parameter can't be smaller than the network prefix of the range ({$myNetworkPrefix})");
|
||||
}
|
||||
$startIp = $this->getStartAddress();
|
||||
$maxPrefix = $startIp::getNumberOfBits();
|
||||
if ($networkPrefix > $maxPrefix) {
|
||||
throw new OutOfBoundsException("The value of the \$networkPrefix parameter can't be larger than the maximum network prefix of the range ({$maxPrefix})");
|
||||
}
|
||||
switch ($startIp->getAddressType()) {
|
||||
case AddressType::T_IPv4:
|
||||
$one = IPv4::fromBytes(array(0, 0, 0, 1));
|
||||
break;
|
||||
case AddressType::T_IPv6:
|
||||
$one = IPv6::fromBytes(array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1));
|
||||
break;
|
||||
}
|
||||
/** @var \IPLib\Address\AddressInterface $one */
|
||||
$delta = $one->shift($networkPrefix - $maxPrefix);
|
||||
$result = array();
|
||||
while (true) {
|
||||
$range = Subnet::parseString("{$startIp}/{$networkPrefix}");
|
||||
/** @var Subnet $range */
|
||||
if (!$forceSubnet && $this instanceof Pattern) {
|
||||
$range = $range->asPattern() ?: $range;
|
||||
}
|
||||
$result[] = $range;
|
||||
$startIp = $startIp->add($delta);
|
||||
if ($startIp === null || !$this->contains($startIp)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
Vendored
Executable
+354
@@ -0,0 +1,354 @@
|
||||
<?php
|
||||
|
||||
namespace IPLib\Range;
|
||||
|
||||
use IPLib\Address\AddressInterface;
|
||||
use IPLib\Address\IPv4;
|
||||
use IPLib\Address\IPv6;
|
||||
use IPLib\Address\Type as AddressType;
|
||||
use IPLib\ParseStringFlag;
|
||||
use IPLib\Service\BinaryMath;
|
||||
|
||||
/**
|
||||
* Represents an address range in pattern format (only ending asterisks are supported).
|
||||
*
|
||||
* @example 127.0.*.*
|
||||
* @example ::/8
|
||||
*
|
||||
* @phpstan-consistent-constructor
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @since 1.5.0
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.17.0: use the parseString() method instead.
|
||||
* For upgrading:
|
||||
* - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
|
||||
*
|
||||
* @param string|mixed $range
|
||||
* @param bool $supportNonDecimalIPv4
|
||||
*
|
||||
* @return static|null
|
||||
*
|
||||
* @see \IPLib\Range\Pattern::parseString()
|
||||
* @since 1.10.0 added the $supportNonDecimalIPv4 argument
|
||||
*/
|
||||
public static function fromString($range, $supportNonDecimalIPv4 = false)
|
||||
{
|
||||
return static::parseString($range, ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::MAY_INCLUDE_ZONEID | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Try get the range instance starting from its string representation.
|
||||
*
|
||||
* @param string|mixed $range
|
||||
* @param int $flags A combination or zero or more flags
|
||||
*
|
||||
* @return static|null
|
||||
*
|
||||
* @since 1.17.0
|
||||
* @see \IPLib\ParseStringFlag
|
||||
*/
|
||||
public static function parseString($range, $flags = 0)
|
||||
{
|
||||
if (!is_string($range) || strpos($range, '*') === false) {
|
||||
return null;
|
||||
}
|
||||
if ($range === '*.*.*.*') {
|
||||
$fromAddress = IPv4::parseString('0.0.0.0');
|
||||
/** @var \IPLib\Address\IPv4 $fromAddress */
|
||||
$toAddress = IPv4::parseString('255.255.255.255');
|
||||
/** @var \IPLib\Address\IPv4 $toAddress */
|
||||
|
||||
return new static($fromAddress, $toAddress, 4);
|
||||
}
|
||||
if ($range === '*:*:*:*:*:*:*:*') {
|
||||
$fromAddress = IPv6::parseString('::');
|
||||
/** @var \IPLib\Address\IPv6 $fromAddress */
|
||||
$toAddress = IPv6::parseString('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff');
|
||||
/** @var \IPLib\Address\IPv6 $toAddress */
|
||||
|
||||
return new static($fromAddress, $toAddress, 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::parseString(str_replace('*', '0', $range), $flags);
|
||||
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));
|
||||
/** @var \IPLib\Address\IPv4 $toAddress */
|
||||
|
||||
return new static($fromAddress, $toAddress, $asterisksCount);
|
||||
}
|
||||
if (strpos($range, ':') !== false && preg_match('/^[^*]+((?::\*)+)$/', $range, $matches)) {
|
||||
$asterisksCount = strlen($matches[1]) >> 1;
|
||||
$fromAddress = IPv6::parseString(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));
|
||||
/** @var \IPLib\Address\IPv6 $toAddress */
|
||||
|
||||
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);
|
||||
/** @var IPv6 $address */
|
||||
$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()
|
||||
* @since 1.8.0
|
||||
*/
|
||||
public function asSubnet()
|
||||
{
|
||||
return new Subnet($this->getStartAddress(), $this->getEndAddress(), $this->getNetworkPrefix());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \IPLib\Range\RangeInterface::getSize()
|
||||
*/
|
||||
public function getSize()
|
||||
{
|
||||
$fromAddress = $this->fromAddress;
|
||||
$maxPrefix = $fromAddress::getNumberOfBits();
|
||||
$prefix = $this->getNetworkPrefix();
|
||||
|
||||
return pow(2, $maxPrefix - $prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \IPLib\Range\RangeInterface::getExactSize()
|
||||
*/
|
||||
public function getExactSize()
|
||||
{
|
||||
$fromAddress = $this->fromAddress;
|
||||
$maxPrefix = $fromAddress::getNumberOfBits();
|
||||
$prefix = $this->getNetworkPrefix();
|
||||
|
||||
return BinaryMath::getInstance()->pow2string($maxPrefix - $prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \IPLib\Range\RangeInterface::getNetworkPrefix()
|
||||
*/
|
||||
public function getNetworkPrefix()
|
||||
{
|
||||
switch ($this->getAddressType()) {
|
||||
case AddressType::T_IPv4:
|
||||
return 8 * (4 - $this->asterisksCount);
|
||||
case AddressType::T_IPv6:
|
||||
return 16 * (8 - $this->asterisksCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
Executable
+198
@@ -0,0 +1,198 @@
|
||||
<?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
|
||||
*
|
||||
* @phpstan-return \IPLib\Address\Type::T_IPv4|\IPLib\Address\Type::T_IPv6
|
||||
*/
|
||||
public function getAddressType();
|
||||
|
||||
/**
|
||||
* Get the type of range of the IP address.
|
||||
*
|
||||
* @return int|null One of the \IPLib\Range\Type::T_... constants, or null if this range crosses multiple range types
|
||||
*
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public function getRangeType();
|
||||
|
||||
/**
|
||||
* Get the address at a certain offset of this range.
|
||||
*
|
||||
* @param int|numeric-string|mixed $n the offset of the address (support negative offset)
|
||||
*
|
||||
* @return \IPLib\Address\AddressInterface|null return NULL if $n is neither an integer nor a string containing a valid integer, or if the offset out of range
|
||||
*
|
||||
* @since 1.15.0
|
||||
* @since 1.21.0 $n can also be a numeric string
|
||||
*
|
||||
* @example passing 256 to the range 127.0.0.0/16 will result in 127.0.1.0
|
||||
* @example passing -1 to the range 127.0.1.0/16 will result in 127.0.255.255
|
||||
* @example passing 256 to the range 127.0.0.0/24 will result in NULL
|
||||
*/
|
||||
public function getAddressAtOffset($n);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public function containsRange(RangeInterface $range);
|
||||
|
||||
/**
|
||||
* Get the initial address contained in this range.
|
||||
*
|
||||
* @return \IPLib\Address\AddressInterface
|
||||
*
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public function getStartAddress();
|
||||
|
||||
/**
|
||||
* Get the final address contained in this range.
|
||||
*
|
||||
* @return \IPLib\Address\AddressInterface
|
||||
*
|
||||
* @since 1.4.0
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @since 1.8.0
|
||||
*/
|
||||
public function getSubnetMask();
|
||||
|
||||
/**
|
||||
* Get the subnet/CIDR representation of this range.
|
||||
*
|
||||
* @return \IPLib\Range\Subnet
|
||||
*
|
||||
* @since 1.13.0
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @since 1.13.0
|
||||
*/
|
||||
public function asPattern();
|
||||
|
||||
/**
|
||||
* Get the Reverse DNS Lookup Addresses of this IP range.
|
||||
*
|
||||
* @return string[]
|
||||
*
|
||||
* @since 1.13.0
|
||||
*
|
||||
* @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();
|
||||
|
||||
/**
|
||||
* Get the count of addresses contained in this IP range (possibly approximated).
|
||||
*
|
||||
* @return int|float If the number of addresses exceeds PHP_INT_MAX a float containing an approximation will be returned
|
||||
*
|
||||
* @since 1.16.0
|
||||
*/
|
||||
public function getSize();
|
||||
|
||||
/**
|
||||
* Get the exact count of addresses contained in this IP range.
|
||||
*
|
||||
* @return int|numeric-string If the number of addresses exceeds PHP_INT_MAX a string containing the exact number of addresses will be returned
|
||||
*
|
||||
* @since 1.21.0
|
||||
*/
|
||||
public function getExactSize();
|
||||
|
||||
/**
|
||||
* Get the "network prefix", that is how many bits of the address are dedicated to the network portion.
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @since 1.19.0
|
||||
*
|
||||
* @example for 10.0.0.0/24 it's 24
|
||||
* @example for 10.0.0.* it's 24
|
||||
*/
|
||||
public function getNetworkPrefix();
|
||||
|
||||
/**
|
||||
* Split the range into smaller ranges.
|
||||
*
|
||||
* @param int $networkPrefix
|
||||
* @param bool $forceSubnet set to true to always have ranges in "subnet format" (ie 1.2.3.4/5), to false to try to keep the original format if possible (that is, pattern to pattern, single to single)
|
||||
*
|
||||
* @throws \OutOfBoundsException if $networkPrefix is not valid
|
||||
*
|
||||
* @return \IPLib\Range\RangeInterface[]
|
||||
*
|
||||
* @since 1.19.0
|
||||
*/
|
||||
public function split($networkPrefix, $forceSubnet = false);
|
||||
}
|
||||
Vendored
Executable
+266
@@ -0,0 +1,266 @@
|
||||
<?php
|
||||
|
||||
namespace IPLib\Range;
|
||||
|
||||
use IPLib\Address\AddressInterface;
|
||||
use IPLib\Address\IPv4;
|
||||
use IPLib\Address\Type as AddressType;
|
||||
use IPLib\Factory;
|
||||
use IPLib\ParseStringFlag;
|
||||
|
||||
/**
|
||||
* Represents a single address (eg a range that contains just one address).
|
||||
*
|
||||
* @example 127.0.0.1
|
||||
* @example ::1
|
||||
*
|
||||
* @phpstan-consistent-constructor
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.17.0: use the parseString() method instead.
|
||||
* For upgrading:
|
||||
* - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
|
||||
*
|
||||
* @param string|mixed $range
|
||||
* @param bool $supportNonDecimalIPv4
|
||||
*
|
||||
* @return static|null
|
||||
*
|
||||
* @see \IPLib\Range\Single::parseString()
|
||||
* @since 1.10.0 added the $supportNonDecimalIPv4 argument
|
||||
*/
|
||||
public static function fromString($range, $supportNonDecimalIPv4 = false)
|
||||
{
|
||||
return static::parseString($range, ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::MAY_INCLUDE_ZONEID | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Try get the range instance starting from its string representation.
|
||||
*
|
||||
* @param string|mixed $range
|
||||
* @param int $flags A combination or zero or more flags
|
||||
*
|
||||
* @return static|null
|
||||
*
|
||||
* @see \IPLib\ParseStringFlag
|
||||
* @since 1.17.0
|
||||
*/
|
||||
public static function parseString($range, $flags = 0)
|
||||
{
|
||||
$result = null;
|
||||
$flags = (int) $flags;
|
||||
$address = Factory::parseAddressString($range, $flags);
|
||||
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
|
||||
*
|
||||
* @since 1.2.0
|
||||
*/
|
||||
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::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()
|
||||
{
|
||||
return new Subnet($this->address, $this->address, $this->getNetworkPrefix());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \IPLib\Range\RangeInterface::getSize()
|
||||
*/
|
||||
public function getSize()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \IPLib\Range\RangeInterface::getExactSize()
|
||||
*/
|
||||
public function getExactSize()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \IPLib\Range\RangeInterface::getNetworkPrefix()
|
||||
*/
|
||||
public function getNetworkPrefix()
|
||||
{
|
||||
switch ($this->getAddressType()) {
|
||||
case AddressType::T_IPv4:
|
||||
return 32;
|
||||
case AddressType::T_IPv6:
|
||||
return 128;
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
Executable
+373
@@ -0,0 +1,373 @@
|
||||
<?php
|
||||
|
||||
namespace IPLib\Range;
|
||||
|
||||
use IPLib\Address\AddressInterface;
|
||||
use IPLib\Address\IPv4;
|
||||
use IPLib\Address\Type as AddressType;
|
||||
use IPLib\Factory;
|
||||
use IPLib\ParseStringFlag;
|
||||
use IPLib\Service\BinaryMath;
|
||||
|
||||
/**
|
||||
* Represents an address range in subnet format (eg CIDR).
|
||||
*
|
||||
* @example 127.0.0.1/32
|
||||
* @example ::/8
|
||||
*
|
||||
* @phpstan-consistent-constructor
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @since 1.5.0
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.17.0: use the parseString() method instead.
|
||||
* For upgrading:
|
||||
* - if $supportNonDecimalIPv4 is true, use the ParseStringFlag::IPV4_MAYBE_NON_DECIMAL flag
|
||||
*
|
||||
* @param string|mixed $range
|
||||
* @param bool $supportNonDecimalIPv4
|
||||
*
|
||||
* @return static|null
|
||||
*
|
||||
* @see \IPLib\Range\Subnet::parseString()
|
||||
* @since 1.10.0 added the $supportNonDecimalIPv4 argument
|
||||
*/
|
||||
public static function fromString($range, $supportNonDecimalIPv4 = false)
|
||||
{
|
||||
return static::parseString($range, ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::MAY_INCLUDE_ZONEID | ($supportNonDecimalIPv4 ? ParseStringFlag::IPV4_MAYBE_NON_DECIMAL : 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Try get the range instance starting from its string representation.
|
||||
*
|
||||
* @param string|mixed $range
|
||||
* @param int $flags A combination or zero or more flags
|
||||
*
|
||||
* @return static|null
|
||||
*
|
||||
* @see \IPLib\ParseStringFlag
|
||||
* @since 1.17.0
|
||||
*/
|
||||
public static function parseString($range, $flags = 0)
|
||||
{
|
||||
if (!is_string($range)) {
|
||||
return null;
|
||||
}
|
||||
$parts = explode('/', $range);
|
||||
if (count($parts) !== 2) {
|
||||
return null;
|
||||
}
|
||||
$flags = (int) $flags;
|
||||
if (strpos($parts[0], ':') === false && $flags & ParseStringFlag::IPV4SUBNET_MAYBE_COMPACT) {
|
||||
$missingDots = 3 - substr_count($parts[0], '.');
|
||||
if ($missingDots > 0) {
|
||||
$parts[0] .= str_repeat('.0', $missingDots);
|
||||
}
|
||||
}
|
||||
$address = Factory::parseAddressString($parts[0], $flags);
|
||||
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 & (int) bindec(str_pad(str_repeat('1', $startSameBits), 8, '0', STR_PAD_RIGHT));
|
||||
$differentBytesEnd[0] = $differentBytesStart[0] + (int) bindec(str_repeat('1', 8 - $startSameBits));
|
||||
}
|
||||
$fromAddress = Factory::addressFromBytes(array_merge($sameBytes, $differentBytesStart));
|
||||
/** @var \IPLib\Address\AddressInterface $fromAddress */
|
||||
$toAddress = Factory::addressFromBytes(array_merge($sameBytes, $differentBytesEnd));
|
||||
/** @var \IPLib\Address\AddressInterface $toAddress */
|
||||
|
||||
return new static($fromAddress, $toAddress, $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;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \IPLib\Range\RangeInterface::asPattern()
|
||||
* @since 1.8.0
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public static function get6to4()
|
||||
{
|
||||
if (self::$sixToFour === null) {
|
||||
$subnet = self::parseString('2002::/16');
|
||||
/** @var Subnet $subnet */
|
||||
self::$sixToFour = $subnet;
|
||||
}
|
||||
|
||||
return self::$sixToFour;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \IPLib\Range\RangeInterface::getNetworkPrefix()
|
||||
* @since 1.7.0
|
||||
*/
|
||||
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());
|
||||
/** @var string $initialPointer */
|
||||
$chunks = explode('.', $initialPointer, 2);
|
||||
for ($index = 0; $index < $numVariants; $index++) {
|
||||
if ($index !== 0) {
|
||||
$chunks[0] = $isHex ? dechex(1 + (int) hexdec($chunks[0])) : (string) (1 + (int) $chunks[0]);
|
||||
}
|
||||
$result[] = implode('.', $chunks);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \IPLib\Range\RangeInterface::getSize()
|
||||
*/
|
||||
public function getSize()
|
||||
{
|
||||
$fromAddress = $this->fromAddress;
|
||||
$maxPrefix = $fromAddress::getNumberOfBits();
|
||||
$prefix = $this->getNetworkPrefix();
|
||||
|
||||
return pow(2, $maxPrefix - $prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see \IPLib\Range\RangeInterface::getExactSize()
|
||||
*/
|
||||
public function getExactSize()
|
||||
{
|
||||
$fromAddress = $this->fromAddress;
|
||||
$maxPrefix = $fromAddress::getNumberOfBits();
|
||||
$prefix = $this->getNetworkPrefix();
|
||||
|
||||
return BinaryMath::getInstance()->pow2string($maxPrefix - $prefix);
|
||||
}
|
||||
}
|
||||
Vendored
Executable
+152
@@ -0,0 +1,152 @@
|
||||
<?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
|
||||
*
|
||||
* @since 1.10.0
|
||||
*/
|
||||
const T_CGNAT = 14;
|
||||
|
||||
/**
|
||||
* Get the name of a type.
|
||||
*
|
||||
* @param int|mixed $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)', print_r($type, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
Executable
+246
@@ -0,0 +1,246 @@
|
||||
<?php
|
||||
|
||||
namespace IPLib\Service;
|
||||
|
||||
/**
|
||||
* Helper class to work with unsigned binary integers.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class BinaryMath
|
||||
{
|
||||
/**
|
||||
* @var \IPLib\Service\BinaryMath|null
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* @return \IPLib\Service\BinaryMath
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim the leading zeroes from a non-negative integer represented in binary form.
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute 2 raised to the given exponent.
|
||||
*
|
||||
* If the result fits into a native PHP integer, an int is returned.
|
||||
* If the result exceeds PHP_INT_MAX, a string containing the exact decimal representation is returned.
|
||||
*
|
||||
* @param int $exponent The non-negative exponent
|
||||
*
|
||||
* @return int|numeric-string
|
||||
*/
|
||||
public function pow2string($exponent)
|
||||
{
|
||||
if ($exponent < PHP_INT_SIZE * 8 - 1) {
|
||||
return 1 << $exponent;
|
||||
}
|
||||
$digits = array(1);
|
||||
for ($i = 0; $i < $exponent; $i++) {
|
||||
$carry = 0;
|
||||
foreach ($digits as $index => $digit) {
|
||||
$product = $digit * 2 + $carry;
|
||||
$digits[$index] = $product % 10;
|
||||
$carry = (int) ($product / 10);
|
||||
}
|
||||
if ($carry !== 0) {
|
||||
$digits[] = $carry;
|
||||
}
|
||||
}
|
||||
$result = implode('', array_reverse($digits));
|
||||
/** @var numeric-string $result */
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param numeric-string|mixed $value
|
||||
*
|
||||
* @return numeric-string|'' empty string if $value is not a valid numeric string
|
||||
*/
|
||||
public function normalizeIntegerString($value)
|
||||
{
|
||||
if (!is_string($value) || $value === '') {
|
||||
return '';
|
||||
}
|
||||
$sign = $value[0];
|
||||
if ($sign === '-' || $sign === '+') {
|
||||
$value = substr($value, 1);
|
||||
}
|
||||
$matches = null;
|
||||
if (!preg_match('/^0*([0-9]+)$/', $value, $matches)) {
|
||||
return '';
|
||||
}
|
||||
$numericString = $matches[1];
|
||||
if ($sign === '-' && $numericString !== '0') {
|
||||
$numericString = '-' . $numericString;
|
||||
}
|
||||
/** @var numeric-string $numericString */
|
||||
|
||||
return $numericString;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param numeric-string $value a string that has been normalized with normalizeIntegerString()
|
||||
*
|
||||
* @return numeric-string
|
||||
*/
|
||||
public function add1ToIntegerString($value)
|
||||
{
|
||||
if ($value[0] === '-') {
|
||||
if ($value === '-1') {
|
||||
return '0';
|
||||
}
|
||||
$digits = str_split(substr($value, 1));
|
||||
$i = count($digits) - 1;
|
||||
while ($i >= 0) {
|
||||
if ($digits[$i] !== '0') {
|
||||
$digits[$i] = (string) ((int) $digits[$i] - 1);
|
||||
break;
|
||||
}
|
||||
$digits[$i] = '9';
|
||||
$i--;
|
||||
}
|
||||
$imploded = implode('', $digits);
|
||||
if ($imploded[0] === '0') {
|
||||
$imploded = substr($imploded, 1);
|
||||
}
|
||||
$result = '-' . $imploded;
|
||||
/** @var numeric-string $result */
|
||||
|
||||
return $result; // @phpstan-ignore varTag.nativeType
|
||||
}
|
||||
$digits = str_split($value);
|
||||
$carry = 1;
|
||||
for ($i = count($digits) - 1; $i >= 0; $i--) {
|
||||
$sum = (int) $digits[$i] + $carry;
|
||||
$digits[$i] = (string) ($sum % 10);
|
||||
$carry = (int) ($sum / 10);
|
||||
if ($carry === 0) {
|
||||
break;
|
||||
}
|
||||
if ($i === 0) {
|
||||
array_unshift($digits, (string) $carry);
|
||||
}
|
||||
}
|
||||
$result = implode('', $digits);
|
||||
/** @var numeric-string $result */
|
||||
|
||||
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 array{string, 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
Vendored
Executable
+254
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
|
||||
namespace IPLib\Service;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @readonly
|
||||
*/
|
||||
class NumberInChunks
|
||||
{
|
||||
const CHUNKSIZE_BYTES = 8;
|
||||
|
||||
const CHUNKSIZE_WORDS = 16;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $negative;
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
*/
|
||||
public $chunks;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $chunkSize;
|
||||
|
||||
/**
|
||||
* @param bool $negative
|
||||
* @param int[] $chunks
|
||||
* @param int $chunkSize
|
||||
*/
|
||||
public function __construct($negative, array $chunks, $chunkSize)
|
||||
{
|
||||
$this->negative = $negative;
|
||||
$this->chunks = $chunks;
|
||||
$this->chunkSize = $chunkSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \InvalidArgumentException if $other has a $chunkSize that's not the same as the $chunkSize of this
|
||||
*
|
||||
* @return \IPLib\Service\NumberInChunks
|
||||
*/
|
||||
public function negate()
|
||||
{
|
||||
return new self($this->chunks === array(0) ? false : !$this->negative, $this->chunks, $this->chunkSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \InvalidArgumentException if $other has a $chunkSize that's not the same as the $chunkSize of this
|
||||
*
|
||||
* @return \IPLib\Service\NumberInChunks
|
||||
*/
|
||||
public function add(NumberInChunks $that)
|
||||
{
|
||||
if ($this->chunkSize !== $that->chunkSize) {
|
||||
throw new InvalidArgumentException('Incompatible chunk size');
|
||||
}
|
||||
if ($this->negative === $that->negative) {
|
||||
return new self($this->negative, self::addChunks($this->chunks, $that->chunks, $this->chunkSize), $this->chunkSize);
|
||||
}
|
||||
if ($that->negative) {
|
||||
list($negative, $chunks) = self::substractChunks($this->chunks, $that->chunks, $this->chunkSize);
|
||||
} else {
|
||||
list($negative, $chunks) = self::substractChunks($that->chunks, $this->chunks, $this->chunkSize);
|
||||
}
|
||||
|
||||
return new self($negative, $chunks, $this->chunkSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $int
|
||||
* @param int $chunkSize
|
||||
*
|
||||
* @return \IPLib\Service\NumberInChunks
|
||||
*/
|
||||
public static function fromInteger($int, $chunkSize)
|
||||
{
|
||||
if ($int === 0) {
|
||||
return new self(false, array(0), $chunkSize);
|
||||
}
|
||||
$negative = $int < 0;
|
||||
if ($negative) {
|
||||
$positiveInt = -$int;
|
||||
/** @var int|float $positiveInt may be float because -PHP_INT_MIN is bigger than PHP_INT_MAX */
|
||||
if (is_float($positiveInt)) {
|
||||
return self::fromNumericString((string) $int, $chunkSize);
|
||||
}
|
||||
$int = $positiveInt;
|
||||
}
|
||||
$bitMask = (1 << $chunkSize) - 1;
|
||||
$chunks = array();
|
||||
while ($int !== 0) {
|
||||
$chunks[] = $int & $bitMask;
|
||||
$int >>= $chunkSize;
|
||||
}
|
||||
|
||||
return new self($negative, array_reverse($chunks), $chunkSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $numericString a string normalized with BinaryMath::normalizeIntegerString()
|
||||
* @param int $chunkSize
|
||||
*
|
||||
* @return \IPLib\Service\NumberInChunks
|
||||
*/
|
||||
public static function fromNumericString($numericString, $chunkSize)
|
||||
{
|
||||
if ($numericString === '0') {
|
||||
return new self(false, array(0), $chunkSize);
|
||||
}
|
||||
$negative = $numericString[0] === '-';
|
||||
if ($negative) {
|
||||
$numericString = substr($numericString, 1);
|
||||
}
|
||||
$chunks = array();
|
||||
while ($numericString !== '0') {
|
||||
$chunks[] = self::modulo($numericString, $chunkSize);
|
||||
$numericString = self::divide($numericString, $chunkSize);
|
||||
}
|
||||
|
||||
return new self($negative, array_reverse($chunks), $chunkSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $numericString
|
||||
* @param int $chunkSize
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private static function modulo($numericString, $chunkSize)
|
||||
{
|
||||
$divisor = 1 << $chunkSize;
|
||||
$carry = 0;
|
||||
$len = strlen($numericString);
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
$digit = (int) $numericString[$i];
|
||||
$carry = ($carry * 10 + $digit) % $divisor;
|
||||
}
|
||||
|
||||
return $carry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $numericString
|
||||
* @param int $chunkSize
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function divide($numericString, $chunkSize)
|
||||
{
|
||||
$divisor = 1 << $chunkSize;
|
||||
$quotient = '';
|
||||
$carry = 0;
|
||||
$len = strlen($numericString);
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
$digit = (int) $numericString[$i];
|
||||
$value = $carry * 10 + $digit;
|
||||
$quotient .= (string) ($value >> $chunkSize);
|
||||
$carry = $value % $divisor;
|
||||
}
|
||||
|
||||
return ltrim($quotient, '0') ?: '0';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $addend1
|
||||
* @param int[] $addend2
|
||||
* @param int $chunkSize
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
private static function addChunks(array $addend1, array $addend2, $chunkSize)
|
||||
{
|
||||
$divisor = 1 << $chunkSize;
|
||||
$result = array();
|
||||
$carry = 0;
|
||||
while ($addend1 !== array() || $addend2 !== array()) {
|
||||
$sum = $carry + (array_pop($addend1) ?: 0) + (array_pop($addend2) ?: 0);
|
||||
$result[] = $sum % $divisor;
|
||||
$carry = $sum >> $chunkSize;
|
||||
}
|
||||
if ($carry !== 0) {
|
||||
$result[] = $carry;
|
||||
}
|
||||
|
||||
return array_reverse($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $minuend
|
||||
* @param int[] $subtrahend
|
||||
* @param int $chunkSize
|
||||
*
|
||||
* @return array{bool, int[]}
|
||||
*/
|
||||
private static function substractChunks(array $minuend, array $subtrahend, $chunkSize)
|
||||
{
|
||||
$minuendCount = count($minuend);
|
||||
$subtrahendCount = count($subtrahend);
|
||||
if ($minuendCount > $subtrahendCount) {
|
||||
$count = $minuendCount;
|
||||
$negative = false;
|
||||
} elseif ($minuendCount < $subtrahendCount) {
|
||||
$count = $subtrahendCount;
|
||||
$negative = true;
|
||||
} else {
|
||||
$count = $minuendCount;
|
||||
$negative = false;
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$delta = $minuend[$i] - $subtrahend[$i];
|
||||
if ($delta === 0) {
|
||||
continue;
|
||||
}
|
||||
if ($delta < 0) {
|
||||
$negative = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($negative) {
|
||||
list($minuend, $subtrahend) = array($subtrahend, $minuend);
|
||||
}
|
||||
$subtrahend = array_pad($subtrahend, -$count, 0);
|
||||
$borrowValue = 1 << $chunkSize;
|
||||
$result = array();
|
||||
$borrow = 0;
|
||||
for ($i = $count - 1; $i >= 0; $i--) {
|
||||
$value = $minuend[$i] - $subtrahend[$i] - $borrow;
|
||||
if ($value < 0) {
|
||||
$value += $borrowValue;
|
||||
$borrow = 1;
|
||||
} else {
|
||||
$borrow = 0;
|
||||
}
|
||||
$result[] = $value;
|
||||
}
|
||||
while (isset($result[1])) {
|
||||
$value = array_pop($result);
|
||||
if ($value !== 0) {
|
||||
$result[] = $value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return array($negative, array_reverse($result));
|
||||
}
|
||||
}
|
||||
Vendored
Executable
+175
@@ -0,0 +1,175 @@
|
||||
<?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 boundaries.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class RangesFromBoundaryCalculator
|
||||
{
|
||||
/**
|
||||
* The BinaryMath instance to be used to perform bitwise operations.
|
||||
*
|
||||
* @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 = BinaryMath::getInstance();
|
||||
$this->setNumBits($numBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the subnets describing all (and only all) the addresses between two boundaries.
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function calculate($start, $end, $position, array &$result)
|
||||
{
|
||||
if ($start === $end) {
|
||||
$result[] = $this->subnetFromBits($start, $this->numBits);
|
||||
|
||||
return;
|
||||
}
|
||||
$startMasked = '';
|
||||
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[] = (int) bindec($byteBits);
|
||||
}
|
||||
$result = Factory::addressFromBytes($bytes);
|
||||
/** @var AddressInterface $result */
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$startAddress = $this->addressFromBits($bits);
|
||||
$numOnes = $this->numBits - $networkPrefix;
|
||||
if ($numOnes === 0) {
|
||||
return new Subnet($startAddress, $startAddress, $networkPrefix);
|
||||
}
|
||||
$endAddress = $this->addressFromBits(substr($bits, 0, -$numOnes) . str_repeat('1', $numOnes));
|
||||
|
||||
return new Subnet($startAddress, $endAddress, $networkPrefix);
|
||||
}
|
||||
}
|
||||
Vendored
Executable
+173
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
namespace IPLib\Service;
|
||||
|
||||
/**
|
||||
* Helper class to work with unsigned integers.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class UnsignedIntegerMath
|
||||
{
|
||||
/**
|
||||
* Convert a string containing a decimal, octal or hexadecimal number into its bytes.
|
||||
*
|
||||
* @param string $value
|
||||
* @param int $numBytes the wanted number of bytes
|
||||
* @param bool $onlyDecimal Only parse decimal numbers
|
||||
*
|
||||
* @return int[]|null
|
||||
*/
|
||||
public function getBytes($value, $numBytes, $onlyDecimal = false)
|
||||
{
|
||||
$m = null;
|
||||
if ($onlyDecimal) {
|
||||
if (preg_match('/^0*(\d+)$/', $value, $m)) {
|
||||
return $this->getBytesFromDecimal($m[1], $numBytes);
|
||||
}
|
||||
} else {
|
||||
if (preg_match('/^0[Xx]0*([0-9A-Fa-f]+)$/', $value, $m)) {
|
||||
return $this->getBytesFromHexadecimal($m[1], $numBytes);
|
||||
}
|
||||
if (preg_match('/^0+([0-7]*)$/', $value, $m)) {
|
||||
return $this->getBytesFromOctal($m[1], $numBytes);
|
||||
}
|
||||
if (preg_match('/^[1-9][0-9]*$/', $value)) {
|
||||
return $this->getBytesFromDecimal($value, $numBytes);
|
||||
}
|
||||
}
|
||||
|
||||
// Not a valid number
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
protected function getMaxSignedInt()
|
||||
{
|
||||
return PHP_INT_MAX;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value never zero-length, never extra leading zeroes
|
||||
* @param int $numBytes
|
||||
*
|
||||
* @return int[]|null
|
||||
*/
|
||||
private function getBytesFromBits($value, $numBytes)
|
||||
{
|
||||
$valueLength = strlen($value);
|
||||
if ($valueLength > $numBytes << 3) {
|
||||
// overflow
|
||||
return null;
|
||||
}
|
||||
$remainderBits = $valueLength % 8;
|
||||
if ($remainderBits !== 0) {
|
||||
$value = str_pad($value, $valueLength + 8 - $remainderBits, '0', STR_PAD_LEFT);
|
||||
}
|
||||
$bytes = array_map('bindec', str_split($value, 8));
|
||||
/** @var int[] $bytes */
|
||||
|
||||
return array_pad($bytes, -$numBytes, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value may be zero-length, never extra leading zeroes
|
||||
* @param int $numBytes
|
||||
*
|
||||
* @return int[]|null
|
||||
*/
|
||||
private function getBytesFromOctal($value, $numBytes)
|
||||
{
|
||||
if ($value === '') {
|
||||
return array_fill(0, $numBytes, 0);
|
||||
}
|
||||
$bits = implode(
|
||||
'',
|
||||
array_map(
|
||||
function ($octalDigit) {
|
||||
return str_pad(decbin((int) octdec($octalDigit)), 3, '0', STR_PAD_LEFT);
|
||||
},
|
||||
str_split($value, 1)
|
||||
)
|
||||
);
|
||||
$bits = ltrim($bits, '0');
|
||||
|
||||
return $bits === '' ? array_fill(0, $numBytes, 0) : self::getBytesFromBits($bits, $numBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value never zero-length, never extra leading zeroes
|
||||
* @param int $numBytes
|
||||
*
|
||||
* @return int[]|null
|
||||
*/
|
||||
private function getBytesFromDecimal($value, $numBytes)
|
||||
{
|
||||
$valueLength = strlen($value);
|
||||
$maxSignedIntLength = strlen((string) $this->getMaxSignedInt());
|
||||
if ($valueLength < $maxSignedIntLength) {
|
||||
return $this->getBytesFromBits(decbin((int) $value), $numBytes);
|
||||
}
|
||||
// Divide by two, so that we have 1 less bit
|
||||
$carry = 0;
|
||||
$halfValue = ltrim(
|
||||
implode(
|
||||
'',
|
||||
array_map(
|
||||
function ($digit) use (&$carry) {
|
||||
$number = $carry + (int) $digit;
|
||||
$carry = ($number % 2) * 10;
|
||||
|
||||
return (string) $number >> 1;
|
||||
},
|
||||
str_split($value, 1)
|
||||
)
|
||||
),
|
||||
'0'
|
||||
);
|
||||
$halfValueBytes = $this->getBytesFromDecimal($halfValue, $numBytes);
|
||||
if ($halfValueBytes === null) {
|
||||
return null;
|
||||
}
|
||||
$carry = $carry === 0 ? 0 : 1;
|
||||
$result = array_fill(0, $numBytes, 0);
|
||||
for ($index = $numBytes - 1; $index >= 0; $index--) {
|
||||
$byte = $carry + ($halfValueBytes[$index] << 1);
|
||||
if ($byte <= 0xFF) {
|
||||
$carry = 0;
|
||||
} else {
|
||||
$carry = ($byte & ~0xFF) >> 8;
|
||||
$byte -= 0x100;
|
||||
}
|
||||
$result[$index] = $byte;
|
||||
}
|
||||
if ($carry !== 0) {
|
||||
// Overflow
|
||||
return null;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value never zero-length, never extra leading zeroes
|
||||
* @param int $numBytes
|
||||
*
|
||||
* @return int[]|null
|
||||
*/
|
||||
private function getBytesFromHexadecimal($value, $numBytes)
|
||||
{
|
||||
$valueLength = strlen($value);
|
||||
if ($valueLength > $numBytes << 1) {
|
||||
// overflow
|
||||
return null;
|
||||
}
|
||||
$value = str_pad($value, $valueLength + $valueLength % 2, '0', STR_PAD_LEFT);
|
||||
$bytes = array_map('hexdec', str_split($value, 2));
|
||||
/** @var int[] $bytes */
|
||||
|
||||
return array_pad($bytes, -$numBytes, 0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user