Giter Site home page Giter Site logo

ip-lib's Introduction

Tests Coverage Status Scrutinizer Code Quality Packagist Downloads Open in Gitpod

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 the latest version, unzip it and add these lines in our PHP files:

require_once 'path/to/iplib/ip-lib.php';

Installation with Composer

Simply run

composer require mlocati/ip-lib

or add these lines to your composer.json file:

"require": {
    "mlocati/ip-lib": "^1"
}

Sample usage

Parse an address

To parse an IPv4 address:

$address = \IPLib\Address\IPv4::parseString('127.0.0.1');

To parse an IPv6 address:

$address = \IPLib\Address\IPv6::parseString('::1');

To parse an address in any format (IPv4 or IPv6):

$address = \IPLib\Factory::parseAddressString('::1');
$address = \IPLib\Factory::parseAddressString('127.0.0.1');

Get the next/previous addresses

$address = \IPLib\Factory::parseAddressString('::1');

// This will print ::
echo (string) $address->getPreviousAddress();

// This will print ::2
echo (string) $address->getNextAddress();

Get the addresses at a specified offset

For addresses:

$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));

For ranges:

$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));

Parse an IP address range

To parse a subnet (CIDR) range:

$range = \IPLib\Range\Subnet::parseString('127.0.0.1/24');
$range = \IPLib\Range\Subnet::parseString('::1/128');

To parse a pattern (asterisk notation) range:

$range = \IPLib\Range\Pattern::parseString('127.0.0.*');
$range = \IPLib\Range\Pattern::parseString('::*');

To parse an address as a range:

$range = \IPLib\Range\Single::parseString('127.0.0.1');
$range = \IPLib\Range\Single::parseString('::1');

To parse a range in any format:

$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:

$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:

$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 the boundaries of a range

$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:

// 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:

// 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();

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:

$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:

$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:

$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:

$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:

$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:

$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 or the IPv4-mapped notation:

$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:

// 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.

Getting the subnet mask for IPv4 ranges

You can use the getSubnetMask() to get the subnet mask for IPv4 ranges:

// 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:

// 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();

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:

$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:

$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:

$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:

$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:

/*
 * 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:

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:

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, 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:

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:

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.

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:

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:

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:

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.:

* 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 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 or a one-time coffee ๐Ÿ˜‰

ip-lib's People

Contributors

cowsay1 avatar detain avatar empornium avatar ernix avatar githubjeka avatar henry-spanka avatar metagusto-opensource avatar mikesimonson avatar mlocati avatar peter279k avatar strausmann avatar tomsim avatar vstelmakh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ip-lib's Issues

`rangeFromBoundaries` does not work with large ranges

Here's an example:

$range = \IPLib\Factory::rangeFromBoundaries('1.1.1.1', '2.2.2.2');
$cidr = (string) $range; // Results in 0.0.0.0/6

The CIDR result is 0.0.0.0/6 in this case, while the correct output would be a list of CIDRs:

1.1.1.1/32
1.1.1.2/31
1.1.1.4/30
1.1.1.8/29
1.1.1.16/28
1.1.1.32/27
1.1.1.64/26
1.1.1.128/25
1.1.2.0/23
1.1.4.0/22
1.1.8.0/21
1.1.16.0/20
1.1.32.0/19
1.1.64.0/18
1.1.128.0/17
1.2.0.0/15
1.4.0.0/14
1.8.0.0/13
1.16.0.0/12
1.32.0.0/11
1.64.0.0/10
1.128.0.0/9
2.0.0.0/15
2.2.0.0/23
2.2.2.0/31
2.2.2.2/32

Can the rangeFromBoundaries method only be used for ranges that can be displayed in a single CIDR?

Implementation for getting a random address

Hey there,

I understand this is more of a specific use case on the end-user, but I was wondering if there were any plans to implement a method that could potentially return a random IP address in a block/subnet.

Thanks,
Ely.

In some cases the toString func will not output a fully shortened IPv6 address.

PHP CLI 7.2.17-0ubuntu0.18.04.1 (cli) (built: Apr 18 2019 14:12:38) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies

Testing on Repl.it:

echo \IPLib\Factory::parseRangeString('2a06:840:e030:86c3:5acd::a:0/112')->asSubnet()->toString(false);
echo \IPLib\Factory::parseRangeString('2a06:840:e030:86c3::a:0/112')->asSubnet()->toString(false);

Output:

2a06:840:e030:86c3:5acd:0:a:0/112
2a06:840:e030:86c3::a:0/112

getComparableString/getComparableEndString like toString

Now when used $range->getComparableStartString() return 172.019.253.000 for example.
If I need use it in ip2long()

function ip_range($start, $end) {
    $start = ip2long($start);
    $end = ip2long($end);
    return array_map('long2ip', range($start, $end) );
}
print_r(ip_range($range->getComparableStartString(), $range->getComparableEndString()));

the result will be Array ( [0] => 0.0.0.0 )

Will be great if $range->getComparableStartString(false) returns 172.19.253.0
like AddressInterface::toString()

Schema for database comparison?

Hi,

I found your wonderful library today and I am trying to understand the 'check to see whether an IP is in a range from a database' functionality.

$sA = $ranger->getComparableStartString();
$eA = $ranger->getComparableEndString();
Assuming 8.8.8.0/24 if IPv4:
string(15) "008.008.008.000"
string(15) "008.008.008.255"
Assuming 2607:f0d0:1000::/40 if IPv6:
string(39) "2607:f0d0:1000:0000:0000:0000:0000:0000"
string(39) "2607:f0d0:10ff:ffff:ffff:ffff:ffff:ffff"

The example query listed in the README is:

select * from ranges
where addressType = :addressType
and :address between rangeFrom and rangeTo

What is the schema used and what DBMS can actually handle this query?

I am pretty sure that MySQL has no awareness of IP addresses.

ISATAP IPv6 address

Hello!
This lib can't handle ISATAP IPv6 address, like 2001:da8:d800:9:200:5efe:101.88.1.77. Is it possible to fix it?

Test code is listed below

<?php
require "vendor/autoload.php";

print_r(IPLib\Factory::addressFromString("2001:da8:d800:9:200:5efe:101.94.1.77") === null);
// 1

Get reverse pointer for IP address

It would great if it was possible to retrieve the reverse pointer domain for an IP.

For example converting 1.2.3.4 to 4.3.2.1.in-addr.arpa for IPv4.

I might PR this in the future but I wanted to leave this request here in case someone wants to pick it up and see if it would be in scope for this lib.

Column Data Types When Storing Address Ranges

Question...

The Readme.md mentions storing an address range in a database such as MySQL or MariaDB using three columns (i.e. addressType, rangeFrom, rangeTo) so that a simple query can be used to check whether a given address is in a given range.

What data types should be used for the rangeFrom and rangeTo columns so that the query performs as expected (i.e. yields the correct results)? Just a string type with a length capable of storing IPv4 and IPv6 string representations including delimiters? VARCHAR(39)?

/*
 * 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();

Map ip for ranges

How about create method to generate map IP by range:

$range->getMap($count = null); // [0.0.0.0....255.255.255.255]
$range->getMap(1); // [0.0.0.0]
$range->getMap(2); // [0.0.0.0 , 0.0.0.1]
// ....

IP Range DNS Lookup

Following the idea of #44, I would love to gave the possibility to get the reverse pointer for an IP range.

Factory::rangeFromString('cafe::/32')->getReverseDNSLookupName();

// 0.0.0.0.e.f.a.c.in-addr.arpa

Is this maybe in the scope of this package? Would love to contribute to this idea.

toString(true) on ipv4 "expands" address

When using toString(true) for an ipv4-address 127.0.0.1 it is converted to 127.000.000.001. This is practical when sorting but ipv4 addresses are never used like that for display or storage (to my knowledge).

Would you be open to adding expanded() and compressed() methods to AddressInterface the expands/compresses ipv6-addresses but leaves ipv4-addresses as is? (Can provide a pull request).

This would be very convenient when working with a list that contain both ipv4 and ipv6-addresses and you want to convert them for display/storage without having to check versions for the addresses.

Convert Mac address to IPv6 link local or with the provider prefix the public ip address

Would the following functions in this Project be the right place ?

  • Convert MAC address to IPv6 Local Link address.
    Example 11:22:33:44:55:66 to fe80::1322:33ff:fe44:5566

  • Convert MAC address and provider IPv6 prefix to a Public IPv6 address.
    Example Mac Address 11:22:33:44:55:66 and Provider Prefix 2001:9e5:61cf:5600:: to 2001:9e5:61cf:5600:1322:33ff:fe44:5566

  • IPv6 address to MAC address
    Example 2001:9e5:61cf:5600:1322:33ff:fe44:5566 to 11:22:33:44:55:66

Here are two example functions https://stackoverflow.com/questions/54748755/how-to-convert-mac-address-into-ipv6-link-local-address-and-vice-versa

arpa to normal ip?

Hello,

there is the possibility to convert an ip into an arpa.
But is there also a way to convert an arpa into an ip?

Thanks

Incomplete support for Non-decimal notation

The library supports some non-decimal notations, but many formats supported by the ping and browsers do not work here using addressFromString($ip, true, true, true)

left is result of ping, right is "valid" ip notation:

  0.0.0.0	0 (short to 1 octet, actually decimal:)
 0.0.0.10	10
  0.0.0.8	010 (octal)
 0.0.0.10	0xa (hex)
127.0.0.1	127.1 (2 octets)
 10.0.0.8	10.010
127.0.0.1	0177.0x01
127.0.0.1	127.0.1 (3 octets)
127.0.0.1	017700000001 (all-in-one octal)
127.0.0.1	00000000000000000000000000000000000000000000000000000017700000001 (all-in-one octal with infinite leading zeroes)
127.0.0.1	0x7f000001 (all-in-one hex)
127.0.0.1	0x00000000000000000000000000000000000000000000000000000007f000001 (all-in-one hex with infinite leading zeroes)
10.8.0.17	10.010.0x000000000000000011

not sure if i missed some

IP range contains maybe wrong

Here a example of the bug(?):

$ipRange = \IPLib\Factory::rangeFromBoundaries('46.222.1.1', '46.223.25.255');

var_dump($ipRange->contains(\IPLib\Factory::addressFromString('46.223.26.1')), (string) $ipRange, (string) $ipRange->getEndAddress());

This shows something like:
true, 46.222.0.0/15, 46.223.255.255
But shouldn't be 46.223.25.255 be the last ip address in the range? I need to check if the given IP is between the range, and currently it is, but i think it should not. Is there a possibility to check whether the given ip is inside of the other two?

bug report] \IPLib\Factory::rangesFromBoundaries() getEndAddress problem

this is problem. thank you.

$ranges = \IPLib\Factory::rangesFromBoundaries('192.168.0.1', '192.168.0.78');
echo sprintf("%-15s %-15s %-15s %-15s\n", 'ip', 'start', 'end', 'realend');
foreach($ranges as $range) {
	$range2 = \IPLib\Range\Subnet::fromString($range . '');
	echo sprintf("%-15s %-15s %-15s %-15s\n", $range, $range->getStartAddress(), $range->getEndAddress(), $range2->getEndAddress());
}
ip              start           end             realend        
192.168.0.1/32  192.168.0.1     192.168.0.1     192.168.0.1    
192.168.0.2/31  192.168.0.2     192.168.0.2     192.168.0.3    
192.168.0.4/30  192.168.0.4     192.168.0.4     192.168.0.7    
192.168.0.8/29  192.168.0.8     192.168.0.8     192.168.0.15   
192.168.0.16/28 192.168.0.16    192.168.0.16    192.168.0.31   
192.168.0.32/27 192.168.0.32    192.168.0.32    192.168.0.63   
192.168.0.64/29 192.168.0.64    192.168.0.64    192.168.0.71   
192.168.0.72/30 192.168.0.72    192.168.0.72    192.168.0.75   
192.168.0.76/31 192.168.0.76    192.168.0.76    192.168.0.77   
192.168.0.78/32 192.168.0.78    192.168.0.78    192.168.0.78   

Some IPv4 patterns not working

I tested the following IPv4 patterns and they didn't work:

  • "127.0.*"
  • "127.*"

Perhaps this is intended, perhaps not. I fixed it by adding the missing ".*" at the end:

$pattern = \preg_replace('/^(\d{1,3})\\.(?:(\d{1,3})\\.\\*|(\\*))$/D', '\\1.\\2\\3.*.*', $pattern);

containsRange checks are not symmetric with explicit subnet masks involved

An edge case for containsRange I ran into; I was expecting both these checks to return true:

use IPLib\Factory;

$a = Factory::rangeFromString("1.2.3.4");
$b = Factory::rangeFromString("1.2.3.4/32");

$a->containsRange($b); // false
$b->containsRange($a); // true

For context, this came up during some blacklisting logic, where I want to check a user-provided input address or range against a predefined list of excluded IPs or ranges. Both sides are 'input' in the sense that they could come from configuration or from users, so either side could conceivably contain an explicit /32 suffix.

How to check two IPs are in the same subnet?

Hello.

I need to ensure that two HTTP requests are from the same subnet (/16).
Couldn't figure out how to check this with your lib :(

My guess was to get rangeFromBoundaries from the first IP and compare it with matches($range) on the second IP. But rangeFromBoundaries second argument requires exact rage specification, I've tried different options, but with no luck...

And were doubts if this method will support ipv6 also...

get all subnets of ipv6 address

hello , I have this ip address 2407:cb00:250/64 and I want to to get all subnets (without ip ) only I want to to retrieve the subnets like this
2407:cb00:250:1/64
2407:cb00:250:2/64
2407:cb00:250:3/64 ...etc
how ca n I get it ?

Unable to install on PHP 8.3

> composer require mlocati/ip-lib
./composer.json has been updated
Running composer update mlocati/ip-lib
Loading composer repositories with package information
Updating dependencies
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - lcobucci/jwt 5.0.0 requires php ~8.1.0 || ~8.2.0 -> your php version (8.3.1) does not satisfy that requirement.
    - laravel/passport v11.8.7 requires lcobucci/jwt ^4.3|^5.0 -> satisfiable by lcobucci/jwt[5.0.0].
    - laravel/passport is locked to version v11.8.7 and an update of this package was not requested.

Next/prev IP

How about to create method to get after or before an IP:

IPLib\Factory::rangeFromString('10.10.0.1/24')
        ->after('10.10.0.5'); // 10.10.0.6/32 (AddressInterface)

IPLib\Factory::rangeFromString('10.10.0.1/24')
        ->before('10.10.0.2'); // 10.10.0.1/32 (AddressInterface)

Support for abbreviated IPv4 subnet notation?

I recently started using this library and while adding unit tests for the new functionality noticed a few corner cases.

Like with IPv6, IPv4, too, has some abbreviated subnet notations. Like with the :: in IPv6 addresses, these are only supported at the end of the IP string and assumes that any missing bits get expanded to all zeros. This abbreviated notation is, for example, supported in both GNU and BSD libc. While this abbreviated notation is therefore widely supported in many utilities that link the libc, I couldn't find RFC documentation that specifically formalizes it, but it is used in RFCs to describe networks, for example in RFC 1338, section 5.

Here are some example abbreviations and their equivalents in the currently supported notation:

  • '127/24' ~ 127.0.0.*
  • '10/8' ~ 10.0.0.0/8
  • '10.10.10/24' ~ 10.10.10.0/24

This is certainly not blocking anyone and one can easily use a different network notation to get the desired result. Many folks don't even know of these abbreviations. Given that implementing this will increase complexity of the library and therefore introduces risks, I can totally understand, if this is considered to be outside of the scope of this library, which would conclude this issue, documenting that these notations are not supported.

If you, on the other hand, would consider reviewing a merge request for this. I'd be willing to invest a few hours to raise one for such a feature + test cases.

Split Subnet

Hi,

Is there a way to split e.g. a /56 IPv6 subnet into /64 subnets?
I haven't been able to find a way to archive this in PHP.

I'd appreciate any help, thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.