Commit ca2e8822 authored by Björn Hjortsten's avatar Björn Hjortsten

Mirror of v1.1.0

Old host https://bitbucket.org/xrstf/ip-utils has removed the repository.
parents
Pipeline #1652 failed with stages
repo: 2a1e606fc368f1fa6e298dd454f1cffc4e672b95
node: 77208641fa9e6063ccb00c8899d157818ad2474f
branch: default
tag: v1.1.0
syntax: glob
vendor/*
d7469bb0c407e423c9d1b49ff2e8980c800871e2 v1.0.0
Copyright (c) 2013 Christoph Mewes
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.
{
"name": "xrstf/ip-utils",
"description": "small library to deal with IPv4 & IPv6 addresses and subnets",
"keywords": [
"ip",
"ipv4",
"ipv6",
"subnet",
"matching"
],
"authors": [
{
"name": "Christoph Mewes",
"homepage": "http://www.xrstf.de/"
}
],
"license": "MIT",
"type": "library",
"require": {
"php": ">=5.3.0"
},
"autoload": {
"psr-0": {
"IpUtils\\": "lib/"
}
},
"extra": {
"branch-alias": {
"dev-default": "1.x-dev"
}
}
}
<?php
/*
* Copyright (c) 2013, Christoph Mewes, http://www.xrstf.de
*
* This file is released under the terms of the MIT license. You can find the
* complete text in the attached LICENSE file or online at:
*
* http://www.opensource.org/licenses/mit-license.php
*/
namespace IpUtils\Address;
use IpUtils\Expression\ExpressionInterface;
interface AddressInterface {
/**
* get fully expanded address
*
* @return string
*/
public function getExpanded();
/**
* get compact address representation
*
* @return string
*/
public function getCompact();
/**
* get IP-specific chunks ([127,000,000,001] for IPv4 or [0000,0000,00ff,00ea,0001,...] for IPv6)
*
* @return array
*/
public function getChunks();
/**
* check whether the address matches a given pattern/range
*
* @param ExpressionInterface $expression
* @return boolean
*/
public function matches(ExpressionInterface $expression);
}
<?php
/*
* Copyright (c) 2013, Christoph Mewes, http://www.xrstf.de
*
* This file is released under the terms of the MIT license. You can find the
* complete text in the attached LICENSE file or online at:
*
* http://www.opensource.org/licenses/mit-license.php
*/
namespace IpUtils\Address;
use IpUtils\Expression\Subnet;
use IpUtils\Expression\ExpressionInterface;
class IPv4 implements AddressInterface {
protected $address;
public function __construct($address) {
if (!self::isValid($address)) {
throw new \UnexpectedValueException('"'.$address.'" is no valid IPv4 address.');
}
$this->address = $address;
}
/**
* @param string $addr
* @return boolean
*/
public static function isValid($address) {
return filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false;
}
/**
* @param int $netmask
* @return boolean
*/
public static function isValidNetmask($netmask) {
return $netmask >= 1 && $netmask <= 32;
}
/**
* @return IPv4
*/
public static function getLoopback() {
return new self('127.0.0.1');
}
/**
* get fully expanded address
*
* @return string
*/
public function getExpanded() {
return $this->address;
}
/**
* get compact address representation
*
* @return string
*/
public function getCompact() {
return $this->getExpanded();
}
/**
* get IP-specific chunks ([127,0,0,1])
*
* @return array
*/
public function getChunks() {
return explode('.', $this->getExpanded());
}
/**
* returns the compact representation
*
* @return string
*/
public function __toString() {
return $this->getCompact();
}
/**
* check whether the IP points to the loopback (localhost) device
*
* @return boolean
*/
public function isLoopback() {
return $this->matches(new Subnet('127.0.0.0/8'));
}
/**
* check whether the IP is inside a private network
*
* @return boolean
*/
public function isPrivate() {
return
$this->matches(new Subnet('10.0.0.0/8')) ||
$this->matches(new Subnet('172.16.0.0/12')) ||
$this->matches(new Subnet('192.168.0.0/16'))
;
}
/**
* check whether the IP is a multicast address
*/
public function isMulticast() {
return $this->matches(new Subnet('224.0.0.0/4'));
}
/**
* check whether the IP is a link-local address
*
* @return boolean
*/
public function isLinkLocal() {
return $this->matches(new Subnet('169.254.1.0/24'));
}
/**
* check whether the address matches a given pattern/range
*
* @param ExpressionInterface $expression
* @return boolean
*/
public function matches(ExpressionInterface $expression) {
return $expression->matches($this);
}
}
<?php
/*
* Copyright (c) 2013, Christoph Mewes, http://www.xrstf.de
*
* This file is released under the terms of the MIT license. You can find the
* complete text in the attached LICENSE file or online at:
*
* http://www.opensource.org/licenses/mit-license.php
*/
namespace IpUtils\Address;
use IpUtils\Expression\Subnet;
use IpUtils\Expression\ExpressionInterface;
class IPv6 implements AddressInterface {
protected $address;
public function __construct($address) {
if (!self::isValid($address)) {
throw new \UnexpectedValueException('"'.$address.'" is no valid IPv6 address.');
}
$this->address = implode(':', array_map(function($b) {
return sprintf('%04x', $b);
}, unpack('n*', inet_pton($address))));
}
/**
* @param string $addr
* @return boolean
*/
public static function isValid($address) {
return filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false;
}
/**
* @param int $netmask
* @return boolean
*/
public static function isValidNetmask($netmask) {
return $netmask >= 1 && $netmask <= 128;
}
/**
* @return IPv6
*/
public static function getLoopback() {
return new self('::1');
}
/**
* get fully expanded address
*
* @return string
*/
public function getExpanded() {
return $this->address;
}
/**
* get compact address representation
*
* @return string
*/
public function getCompact() {
return inet_ntop(inet_pton($this->address));
}
/**
* get IP-specific chunks ([ff,0,0,0,12,2001,ff,....])
*
* @return array
*/
public function getChunks() {
return array_map(function($c) {
return ltrim($c, '0') ?: '0';
}, explode(':', $this->getExpanded()));
}
/**
* returns the compact representation
*
* @return string
*/
public function __toString() {
return $this->getCompact();
}
/**
* check whether the IP points to the loopback (localhost) device
*
* @return boolean
*/
public function isLoopback() {
return $this->matches(new Subnet('::1/128'));
}
/**
* check whether the IP is inside a private network
*
* @return boolean
*/
public function isPrivate() {
return $this->matches(new Subnet('fc00::/7'));
}
/**
* check whether the address matches a given pattern/range
*
* @param ExpressionInterface $expression
* @return boolean
*/
public function matches(ExpressionInterface $expression) {
return $expression->matches($this);
}
}
<?php
/*
* Copyright (c) 2013, Christoph Mewes, http://www.xrstf.de
*
* This file is released under the terms of the MIT license. You can find the
* complete text in the attached LICENSE file or online at:
*
* http://www.opensource.org/licenses/mit-license.php
*/
namespace IpUtils\Exception;
class InvalidExpressionException extends \Exception {
}
<?php
/*
* Copyright (c) 2013, Christoph Mewes, http://www.xrstf.de
*
* This file is released under the terms of the MIT license. You can find the
* complete text in the attached LICENSE file or online at:
*
* http://www.opensource.org/licenses/mit-license.php
*/
namespace IpUtils\Expression;
use IpUtils\Address\AddressInterface;
interface ExpressionInterface {
/**
* check whether the expression matches an address
*
* @param AddressInterface $address
* @return boolean
*/
public function matches(AddressInterface $address);
}
<?php
/*
* Copyright (c) 2013, Christoph Mewes, http://www.xrstf.de
*
* This file is released under the terms of the MIT license. You can find the
* complete text in the attached LICENSE file or online at:
*
* http://www.opensource.org/licenses/mit-license.php
*/
namespace IpUtils\Expression;
use IpUtils\Address\AddressInterface;
use IpUtils\Address\IPv4;
use IpUtils\Address\IPv6;
use IpUtils\Exception\InvalidExpressionException;
class Literal implements ExpressionInterface {
protected $expression;
public function __construct($expression) {
$expression = strtolower(trim($expression));
if (IPv4::isValid($expression)) {
$ip = new IPv4($expression);
}
elseif (IPv6::isValid($expression)) {
$ip = new IPv6($expression);
}
else {
throw new InvalidExpressionException('Expression must be either a valid IPv4 or IPv6 address.');
}
$this->expression = $ip->getCompact();
}
/**
* check whether the expression matches an address
*
* @param AddressInterface $address
* @return boolean
*/
public function matches(AddressInterface $address) {
return $address->getCompact() === $this->expression;
}
}
<?php
/*
* Copyright (c) 2013, Christoph Mewes, http://www.xrstf.de
*
* This file is released under the terms of the MIT license. You can find the
* complete text in the attached LICENSE file or online at:
*
* http://www.opensource.org/licenses/mit-license.php
*/
namespace IpUtils\Expression;
use IpUtils\Address\AddressInterface;
class Pattern implements ExpressionInterface {
protected $expression;
public function __construct($expression) {
$expression = strtolower(trim($expression));
$expression = preg_replace('/\*+/', '*', $expression);
$this->expression = $expression;
}
/**
* check whether the expression matches an address
*
* @param AddressInterface $address
* @return boolean
*/
public function matches(AddressInterface $address) {
$addrChunks = $address->getChunks();
$exprChunks = preg_split('/[.:]/', $this->expression);
if (count($exprChunks) !== count($addrChunks)) {
throw new \UnexpectedValueException('Address and expression do not contain the same amount of chunks. Did you mix IPv4 and IPv6?');
}
foreach ($exprChunks as $idx => $exprChunk) {
$addrChunk = $addrChunks[$idx];
if (strpos($exprChunk, '*') === false) {
// It's okay if the expression contains '.0.' and the IP contains '.000.',
// we just care for the numerical value (and it's also okay to interprete
// IPv4 chunks as hex values, as long as we interprete both as hex).
if (hexdec($addrChunk) !== hexdec($exprChunk)) {
return false;
}
}
else {
$exprChunk = str_replace('*', '[0-9a-f]+?', $exprChunk);
if (!preg_match('/^'.$exprChunk.'$/', $addrChunk)) {
return false;
}
}
}
return true;
}
}
<?php
/*
* Copyright (c) 2013, Christoph Mewes, http://www.xrstf.de
*
* This file is released under the terms of the MIT license. You can find the
* complete text in the attached LICENSE file or online at:
*
* http://www.opensource.org/licenses/mit-license.php
*/
namespace IpUtils\Expression;
use IpUtils\Address\AddressInterface;
use IpUtils\Address\IPv4;
use IpUtils\Address\IPv6;
use IpUtils\Exception\InvalidExpressionException;
class Subnet implements ExpressionInterface {
protected $lower;
protected $netmask;
public function __construct($expression) {
if (strpos($expression, '/') === false) {
throw new InvalidExpressionException('Invalid subnet expression "'.$expression.'" given.');
}
list($lower, $netmask) = explode('/', $expression, 2);
if (strpos($netmask, '.') !== false || strpos($netmask, ':') !== false) {
throw new InvalidExpressionException('Netmasks may not use the IP address format ("127.0.0.1/255.0.0.0").');
}
// check IP format first
if (IPv4::isValid($lower)) {
$ip = new IPv4($lower);
}
elseif (IPv6::isValid($lower)) {
$ip = new IPv6($lower);
}
else {
throw new InvalidExpressionException('Subnet expression "'.$expression.'" contains an invalid IP.');
}
// now we can properly handle the netmask range
$netmask = (int) $netmask;
if (!$ip::isValidNetmask($netmask)) {
throw new InvalidExpressionException('Invalid or out of range netmask given.');
}
$this->lower = $ip;
$this->netmask = $netmask;
}
/**
* check whether the expression matches an address
*
* @param AddressInterface $address
* @return boolean
*/
public function matches(AddressInterface $address) {
$lower = $this->lower->getExpanded();
$addr = $address->getExpanded();
// http://stackoverflow.com/questions/594112/matching-an-ip-to-a-cidr-mask-in-php5
if ($address instanceof IPv4 && $this->lower instanceof IPv4) {
$addr = ip2long($addr);
$lower = ip2long($lower);
$netmask = -1 << (32 - $this->netmask) & ip2long('255.255.255.255');
$lower &= $netmask;
return ($addr & $netmask) == $lower;
}
elseif ($address instanceof IPv6 && $this->lower instanceof IPv6) {
$lower = unpack('n*', inet_pton($lower));
$addr = unpack('n*', inet_pton($addr));
for ($i = 1; $i <= ceil($this->netmask / 16); $i++) {
$left = $this->netmask - 16 * ($i-1);
$left = ($left <= 16) ? $left : 16;
$mask = ~(0xffff >> $left) & 0xffff;
if (($addr[$i] & $mask) != ($lower[$i] & $mask)) {
return false;
}
}
return true;
}
throw new \LogicException('Can only compare IPs of the same version.');
}
}
<?php
/*
* Copyright (c) 2013, Christoph Mewes, http://www.xrstf.de
*
* This file is released under the terms of the MIT license. You can find the
* complete text in the attached LICENSE file or online at:
*
* http://www.opensource.org/licenses/mit-license.php
*/