Rework IP address handling.

This commit is contained in:
flash 2018-03-16 03:01:24 +01:00
parent fdff8e00d9
commit 19d19adec3
13 changed files with 452 additions and 348 deletions

View file

@ -3,7 +3,7 @@ use Carbon\Carbon;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Misuzu\Application;
use Misuzu\Database;
use Misuzu\Net\IP;
use Misuzu\Net\IPAddress;
use Misuzu\Users\User;
use Misuzu\Users\Session;
@ -75,7 +75,7 @@ switch ($mode) {
// Temporary key generation for chat login.
// Should eventually be replaced with a callback login system.
// Also uses different cookies since $httponly is required to be false for these.
$user->last_ip = IP::remote();
$user->last_ip = IPAddress::remote();
$user->user_chat_key = bin2hex(random_bytes(16));
$user->save();

View file

@ -1,107 +0,0 @@
<?php
namespace Misuzu\Net;
use InvalidArgumentException;
/**
* CIDR functions.
* @package Misuzu\Net
* @author flashwave <me@flash.moe>
*/
class CIDR
{
/**
* Matches an IP to a CIDR range.
* @param string $ipAddr
* @param string $network
* @param int|null $mask
* @return bool
*/
public static function match(string $ipAddr, string $network, ?int $mask = null): bool
{
if ($mask === null) {
[$network, $mask] = explode('/', $network);
}
if (empty($mask)) {
throw new InvalidArgumentException('No bitmask supplied.');
}
$ipv = IP::version($ipAddr);
$rangev = IP::version($network);
if (!$ipv || !$rangev || $ipv !== $rangev) {
return false;
}
switch ($ipv) {
case IP::V6:
return static::matchV6($ipAddr, $network, $mask);
case IP::V4:
return static::matchV4($ipAddr, $network, $mask);
default:
throw new InvalidArgumentException('Invalid IP type.');
}
}
/**
* Matches an IPv4 to a CIDR range.
* @param string $ipAddr
* @param string $network
* @param int $mask
* @return bool
*/
private static function matchV4(string $ipAddr, string $network, int $mask): bool
{
$ipAddr = ip2long($ipAddr);
$network = ip2long($network);
$mask = -1 << (32 - $mask);
return ($ipAddr & $mask) === ($network & $mask);
}
/**
* Matches an IPv6 to a CIDR range.
* @param string $ipAddr
* @param string $network
* @param int $mask
* @return bool
*/
private static function matchV6(string $ipAddr, string $network, int $mask): bool
{
$ipAddr = inet_pton($ipAddr);
$network = inet_pton($network);
$mask = static::createV6Mask($mask);
return ($ipAddr & $mask) === ($network & $mask);
}
/**
* Converts an IPv6 mask to bytes.
* @param int $mask
* @return string
*/
private static function createV6Mask(int $mask): string
{
$range = str_repeat('f', $mask / 4);
switch ($mask % 4) {
case 1:
$range .= '8';
break;
case 2:
$range .= 'c';
break;
case 3:
$range .= 'e';
break;
}
$range = str_pad($range, 32, '0');
$range = pack('H*', $range);
return $range;
}
}

View file

@ -1,82 +0,0 @@
<?php
namespace Misuzu\Net;
/**
* IP functions.
* @package Misuzu\Net
* @author flashwave <me@flash.moe>
*/
class IP
{
public const V4 = 4;
public const V6 = 6;
/**
* Attempts to get the remote ip address, falls back to IPv6 localhost.
* @return string
*/
public static function remote(string $fallback = '::1'): string
{
return $_SERVER['REMOTE_ADDR'] ?? $fallback;
}
/**
* Detects IP version.
* @param string $ip
* @return int
*/
public static function version(string $ip): int
{
if (!filter_var($ip, FILTER_VALIDATE_IP)) {
return 0;
}
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
return static::V6;
}
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
return static::V4;
}
return 0;
}
/**
* Converts a printable IP address into an packed binary string.
* @param string $ip
* @throws NetInvalidAddressException
* @return string
*/
public static function unpack(string $ip): string
{
$ipv = static::version($ip);
if ($ipv === 6) {
return current(unpack('A16', inet_pton($ip)));
}
if ($ipv === 4) {
return current(unpack('A4', inet_pton($ip)));
}
throw new NetInvalidAddressException;
}
/**
* Converts a binary unpacked IP to a printable unpacked IP.
* @param string $bin
* @throws NetAddressTypeException
* @return string
*/
public static function pack(string $bin): string
{
$len = strlen($bin);
if ($len !== 4 && $len !== 16) {
throw new NetAddressTypeException;
}
return inet_ntop(pack("A{$len}", $bin));
}
}

141
src/Net/IPAddress.php Normal file
View file

@ -0,0 +1,141 @@
<?php
namespace Misuzu\Net;
use InvalidArgumentException;
/**
* IP Address object.
* @package Misuzu\Net
* @author flashwave <me@flash.moe>
*/
final class IPAddress
{
private const FALLBACK_ADDRESS = '::1';
public const UNKNOWN_VERSION = 0;
public const V4 = 4;
public const V6 = 6;
public const BYTE_COUNT = [
self::V4 => 4,
self::V6 => 16,
];
private $ipVersion = self::UNKNOWN_VERSION;
private $ipRaw = null;
public function getVersion(): int
{
return $this->ipVersion;
}
public function getRaw(): string
{
return $this->ipRaw;
}
public function getString(): string
{
return inet_ntop($this->ipRaw);
}
public function __construct(int $version, string $rawIp)
{
if (!array_key_exists($version, self::BYTE_COUNT)) {
throw new InvalidArgumentException('Invalid IP version provided.');
}
if (strlen($rawIp) !== self::BYTE_COUNT[$version]) {
throw new InvalidArgumentException('Binary IP was of invalid length.');
}
$this->ipVersion = $version;
$this->ipRaw = $rawIp;
}
public function compareTo(IPAddress $other): int
{
if ($other->getVersion() !== $this->getVersion()) {
throw new InvalidArgumentException('Both addresses must be of the same version.');
}
$parts_this = array_values(unpack('N*', $this->getRaw()));
$parts_other = array_values(unpack('N*', $other->getRaw()));
$size = count($parts_this);
if ($size !== count($parts_other)) {
throw new InvalidArgumentException('Addresses varied in length. (if you touched $ipRaw, i will fight you)');
}
for ($i = 0; $i < $size; $i++) {
$result = $parts_other[$i] <=> $parts_this[$i];
if ($result !== 0) {
return $result;
}
}
return 0;
}
public static function remote(string $fallbackAddress = self::FALLBACK_ADDRESS): IPAddress
{
try {
return self::fromString($_SERVER['REMOTE_ADDR'] ?? $fallbackAddress);
} catch (InvalidArgumentException $ex) {
return self::fromString($fallbackAddress);
}
}
public static function fromRaw(string $rawIp): IPAddress
{
$version = self::detectVersionFromRaw($rawIp);
if ($version === self::UNKNOWN_VERSION) {
throw new InvalidArgumentException('Invalid raw IP address supplied.');
}
return new static($version, $rawIp);
}
public static function fromString(string $ipAddress): IPAddress
{
$version = self::detectVersionFromString($ipAddress);
if (!array_key_exists($version, self::BYTE_COUNT)) {
throw new InvalidArgumentException('Invalid IP address supplied.');
}
return new static($version, inet_pton($ipAddress));
}
public static function detectVersionFromRaw(string $rawIp): int
{
$rawLength = strlen($rawIp);
foreach (self::BYTE_COUNT as $version => $length) {
if ($rawLength === $length) {
return $version;
}
}
return self::UNKNOWN_VERSION;
}
public static function detectVersionFromString(string $ipAddress): int
{
if (filter_var($ipAddress, FILTER_VALIDATE_IP) === false) {
return self::UNKNOWN_VERSION;
}
if (filter_var($ipAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false) {
return self::V6;
}
if (filter_var($ipAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false) {
return self::V4;
}
return self::UNKNOWN_VERSION;
}
}

121
src/Net/IPAddressRange.php Normal file
View file

@ -0,0 +1,121 @@
<?php
namespace Misuzu\Net;
use InvalidArgumentException;
final class IPAddressRange
{
private $maskAddress;
private $cidrLength;
public function getMaskAddress(): IPAddress
{
return $this->maskAddress;
}
public function getCidrLength(): int
{
return $this->cidrLength;
}
public function __construct(IPAddress $maskAddress, int $cidrLength)
{
if ($cidrLength > IPAddress::BYTE_COUNT[$maskAddress->getVersion()] * 8) {
throw new InvalidArgumentException('CIDR length is out of range.');
}
$this->maskAddress = $maskAddress;
$this->cidrLength = $cidrLength;
}
public function getMaskedString(): string
{
return $this->getMaskAddress()->getString() . '/' . $this->getCidrLength();
}
public function match(IPAddress $ipAddress, bool $explicitExceptions = false): bool
{
if ($ipAddress->getVersion() !== $this->getMaskAddress()->getVersion()) {
if ($explicitExceptions) {
throw new InvalidArgumentException('Both addresses must be of the same version.');
}
return false;
}
$ipParts = array_values(unpack('N*', $ipAddress->getRaw()));
$maskParts = array_values(unpack('N*', $this->getMaskAddress()->getRaw()));
$parts = count($ipParts);
if ($parts !== count($maskParts)) {
if ($explicitExceptions) {
throw new InvalidArgumentException('Both addresses must be of the same version (failed 1).');
}
return false;
}
for ($i = 0; $i < $parts; $i++) {
$ipParts[$i] = $ipParts[$i] & $maskParts[$i];
}
return $this->getMaskAddress()->getRaw() === pack('N*', ...$ipParts);
}
public static function fromMaskedString(string $maskedString): IPAddressRange
{
if (strpos($maskedString, '/') === false) {
throw new InvalidArgumentException('Invalid masked string.');
}
[$maskedAddress, $cidrLength] = explode('/', $maskedString, 2);
$maskedAddress = IPAddress::fromString($maskedAddress);
$cidrLength = (int)$cidrLength;
return new static($maskedAddress, $cidrLength);
}
// very uncertain about this logic in regards to any ip larger than 32 bits
// if you _do_ know what you're doing, review this and call me an idiot please
public static function fromRangeString(string $rangeString): IPAddressRange
{
if (strpos($rangeString, '-') === false) {
throw new InvalidArgumentException('Invalid range string.');
}
[$rangeStart, $rangeEnd] = explode('-', $rangeString, 2);
$rangeStart = IPAddress::fromString($rangeStart);
$rangeEnd = IPAddress::fromString($rangeEnd);
// implicitly performs a version compare as well, throws an exception if different
if ($rangeStart->compareTo($rangeEnd) < 1) {
throw new InvalidArgumentException('Range start was larger (or equal) to the range end.');
}
$partsStart = array_values(unpack('N*', $rangeStart->getRaw()));
$partsEnd = array_values(unpack('N*', $rangeEnd->getRaw()));
$parts = count($partsStart);
if ($parts !== count($partsEnd)) {
throw new InvalidArgumentException('Range start was larger (or equal) to the range end (failed 1).');
}
$bits = $parts * 32;
$mask = array_fill(0, $parts, 0);
for ($i = 0; $i < $parts; $i++) {
$diffs = $partsStart[$i] ^ $partsEnd[$i];
while ($diffs != 0) {
$diffs >>= 1;
$bits -= 1;
$mask[$i] = ($mask[$i] << 1) | 1;
}
$mask[$i] = $partsStart[$i] & ~$mask[$i];
}
$mask = pack('N*', ...$mask);
return new static(new IPAddress($rangeStart->getVersion(), $mask), $bits);
}
}

View file

@ -1,11 +0,0 @@
<?php
namespace Misuzu\Net;
/**
* Thrown when Net doesn't know how to handle the IP type.
* @package Misuzu\Net
* @author flashwave <me@flash.moe>
*/
class NetAddressTypeException extends NetException
{
}

View file

@ -1,11 +0,0 @@
<?php
namespace Misuzu\Net;
/**
* Holds the base net exception.
* @package Misuzu\Net
* @author flashwave <me@flash.moe>
*/
class NetException extends \Exception
{
}

View file

@ -1,11 +0,0 @@
<?php
namespace Misuzu\Net;
/**
* Thrown when Net has to handle an invalid IP.
* @package Misuzu\Net
* @author flashwave <me@flash.moe>
*/
class NetInvalidAddressException extends NetException
{
}

View file

@ -3,7 +3,7 @@ namespace Misuzu\Users;
use Carbon\Carbon;
use Misuzu\Model;
use Misuzu\Net\IP;
use Misuzu\Net\IPAddress;
class Session extends Model
{
@ -14,9 +14,9 @@ class Session extends Model
User $user,
?string $userAgent = null,
?Carbon $expires = null,
?string $ipAddress = null
?IPAddress $ipAddress = null
): Session {
$ipAddress = $ipAddress ?? IP::remote();
$ipAddress = $ipAddress ?? IPAddress::remote();
$userAgent = $userAgent ?? 'Misuzu';
$expires = $expires ?? Carbon::now()->addMonth();
@ -41,14 +41,14 @@ class Session extends Model
return $this->expires_on->isPast();
}
public function getSessionIpAttribute(string $ipAddress): string
public function getSessionIpAttribute(string $ipAddress): IPAddress
{
return IP::pack($ipAddress);
return IPAddress::fromRaw($ipAddress);
}
public function setSessionIpAttribute(string $ipAddress): void
public function setSessionIpAttribute(IPAddress $ipAddress): void
{
$this->attributes['session_ip'] = IP::unpack($ipAddress);
$this->attributes['session_ip'] = $ipAddress->getRaw();
}
public function user()

View file

@ -4,7 +4,7 @@ namespace Misuzu\Users;
use Illuminate\Database\Eloquent\SoftDeletes;
use Misuzu\Database;
use Misuzu\Model;
use Misuzu\Net\IP;
use Misuzu\Net\IPAddress;
class User extends Model
{
@ -24,9 +24,9 @@ class User extends Model
string $username,
string $password,
string $email,
?string $ipAddress = null
?IPAddress $ipAddress = null
): User {
$ipAddress = $ipAddress ?? IP::remote();
$ipAddress = $ipAddress ?? IPAddress::remote();
$user = new User;
$user->username = $username;
@ -34,7 +34,7 @@ class User extends Model
$user->email = $email;
$user->register_ip = $ipAddress;
$user->last_ip = $ipAddress;
$user->user_country = get_country_code($ipAddress);
$user->user_country = get_country_code($ipAddress->getString());
$user->save();
return $user;
@ -139,24 +139,24 @@ class User extends Model
}
}
public function getRegisterIpAttribute(string $ipAddress): string
public function getRegisterIpAttribute(string $ipAddress): IPAddress
{
return IP::pack($ipAddress);
return IPAddress::fromRaw($ipAddress);
}
public function setRegisterIpAttribute(string $ipAddress): void
public function setRegisterIpAttribute(IPAddress $ipAddress): void
{
$this->attributes['register_ip'] = IP::unpack($ipAddress);
$this->attributes['register_ip'] = $ipAddress->getRaw();
}
public function getLastIpAttribute(string $ipAddress): string
{
return IP::pack($ipAddress);
return IPAddress::fromRaw($ipAddress);
}
public function setLastIpAttribute(string $ipAddress): void
public function setLastIpAttribute(IPAddress $ipAddress): void
{
$this->attributes['last_ip'] = IP::unpack($ipAddress);
$this->attributes['last_ip'] = $ipAddress->getRaw();
}
public function setPasswordAttribute(string $password): void

View file

@ -1,29 +0,0 @@
<?php
namespace MisuzuTests;
use PHPUnit\Framework\TestCase;
use Misuzu\Net\CIDR;
class CIDRTest extends TestCase
{
public function testIPv4()
{
$this->assertTrue(CIDR::match('104.27.135.189', '104.16.0.0/12'));
$this->assertTrue(CIDR::match('104.27.154.200', '104.16.0.0', 12));
$this->assertTrue(CIDR::match('104.28.9.4', '104.16.0.0/12'));
$this->assertTrue(CIDR::match('104.27.135.189', '104.27.115.10', 12));
$this->assertTrue(CIDR::match('104.27.154.200', '104.27.154.20/12'));
$this->assertTrue(CIDR::match('104.28.9.4', '104.28.9.8', 12));
}
// public function testIPv6()
// {
// // IPv6 matching is broken, yay
// $this->assertTrue(CIDR::match('2400:cb00:2048:1:0:0:681b:9ac8', '2400:cb00::', 32));
// $this->assertTrue(CIDR::match('2400:cb00:2048:1:0:0:681b:9ac8', '2400:cb00:2048:1:0:0:681b:5341/32'));
// $this->assertTrue(CIDR::match('2400:cb00:2048:1:0:0:681c:804', '2400:cb00::/32'));
// $this->assertTrue(CIDR::match('2400:cb00:2048:1:0:0:681c:804', '2400:cb00:2048:1:0:0::804/64'));
// $this->assertTrue(CIDR::match('2400:cb00:2048:1:0:0:681b:86bd', '2400:cb00::/32'));
// $this->assertTrue(CIDR::match('2400:cb00:2048:1:0:0:681f:5e2a', '2400:cb00::/16'));
// }
}

170
tests/IPAddressTest.php Normal file
View file

@ -0,0 +1,170 @@
<?php
namespace MisuzuTests;
use PHPUnit\Framework\TestCase;
use Misuzu\Net\IPAddress;
use Misuzu\Net\IPAddressRange;
class IPAddressTest extends TestCase
{
public function testVersion()
{
$this->assertEquals(IPAddress::V4, IPAddress::detectVersionFromString('127.0.0.1'));
$this->assertEquals(IPAddress::V4, IPAddress::detectVersionFromRaw(hex2bin('7f000001')));
$this->assertEquals(IPAddress::V4, IPAddress::detectVersionFromString('104.27.135.189'));
$this->assertEquals(IPAddress::V4, IPAddress::detectVersionFromRaw(hex2bin('681b87bd')));
$this->assertEquals(IPAddress::V4, IPAddress::detectVersionFromString('104.27.154.200'));
$this->assertEquals(IPAddress::V4, IPAddress::detectVersionFromRaw(hex2bin('681b9ac8')));
$this->assertEquals(IPAddress::V4, IPAddress::detectVersionFromString('104.28.9.4'));
$this->assertEquals(IPAddress::V4, IPAddress::detectVersionFromRaw(hex2bin('681c0904')));
$this->assertEquals(IPAddress::V6, IPAddress::detectVersionFromString('::1'));
$this->assertEquals(
IPAddress::V6,
IPAddress::detectVersionFromRaw(hex2bin('00000000000000000000000000000001'))
);
$this->assertEquals(IPAddress::V6, IPAddress::detectVersionFromString('2400:cb00:2048:1:0:0:681b:9ac8'));
$this->assertEquals(
IPAddress::V6,
IPAddress::detectVersionFromRaw(hex2bin('2400cb002048000100000000681b9ac8'))
);
$this->assertEquals(IPAddress::V6, IPAddress::detectVersionFromString('2400:cb00:2048:1:0:0:681c:804'));
$this->assertEquals(
IPAddress::V6,
IPAddress::detectVersionFromRaw(hex2bin('2400cb002048000100000000681c0804'))
);
$this->assertEquals(IPAddress::V6, IPAddress::detectVersionFromString('2400:cb00:2048:1:0:0:681b:86bd'));
$this->assertEquals(
IPAddress::V6,
IPAddress::detectVersionFromRaw(hex2bin('2400cb002048000100000000681b86bd'))
);
$this->assertEquals(IPAddress::V6, IPAddress::detectVersionFromString('2400:cb00:2048:1:0:0:681f:5e2a'));
$this->assertEquals(
IPAddress::V6,
IPAddress::detectVersionFromRaw(hex2bin('2400cb002048000100000000681f5e2a'))
);
$this->assertEquals(IPAddress::UNKNOWN_VERSION, IPAddress::detectVersionFromString('not an ip address'));
$this->assertEquals(IPAddress::UNKNOWN_VERSION, IPAddress::detectVersionFromString('256.256.256.256'));
$this->assertEquals(IPAddress::UNKNOWN_VERSION, IPAddress::detectVersionFromRaw('invalid'));
}
public function testString()
{
$this->assertEquals(hex2bin('7f000001'), IPAddress::fromString('127.0.0.1')->getRaw());
$this->assertEquals(hex2bin('681b87bd'), IPAddress::fromString('104.27.135.189')->getRaw());
$this->assertEquals(hex2bin('681b9ac8'), IPAddress::fromString('104.27.154.200')->getRaw());
$this->assertEquals(hex2bin('681c0904'), IPAddress::fromString('104.28.9.4')->getRaw());
$this->assertEquals(
hex2bin('00000000000000000000000000000001'),
IPAddress::fromString('::1')->getRaw()
);
$this->assertEquals(
hex2bin('2400cb002048000100000000681b9ac8'),
IPAddress::fromString('2400:cb00:2048:1:0:0:681b:9ac8')->getRaw()
);
$this->assertEquals(
hex2bin('2400cb002048000100000000681c0804'),
IPAddress::fromString('2400:cb00:2048:1:0:0:681c:804')->getRaw()
);
$this->assertEquals(
hex2bin('2400cb002048000100000000681b86bd'),
IPAddress::fromString('2400:cb00:2048:1:0:0:681b:86bd')->getRaw()
);
$this->assertEquals(
hex2bin('2400cb002048000100000000681f5e2a'),
IPAddress::fromString('2400:cb00:2048:1:0:0:681f:5e2a')->getRaw()
);
}
public function testRaw()
{
$this->assertEquals('127.0.0.1', IPAddress::fromRaw(hex2bin('7f000001'))->getString());
$this->assertEquals('104.27.135.189', IPAddress::fromRaw(hex2bin('681b87bd'))->getString());
$this->assertEquals('104.27.154.200', IPAddress::fromRaw(hex2bin('681b9ac8'))->getString());
$this->assertEquals('104.28.9.4', IPAddress::fromRaw(hex2bin('681c0904'))->getString());
$this->assertEquals(
'::1',
IPAddress::fromRaw(hex2bin('00000000000000000000000000000001'))->getString()
);
$this->assertEquals(
IPAddress::fromRaw(hex2bin('2400cb002048000100000000681b9ac8'))->getString(),
'2400:cb00:2048:1::681b:9ac8'
);
$this->assertEquals(
'2400:cb00:2048:1::681c:804',
IPAddress::fromRaw(hex2bin('2400cb002048000100000000681c0804'))->getString()
);
$this->assertEquals(
'2400:cb00:2048:1::681b:86bd',
IPAddress::fromRaw(hex2bin('2400cb002048000100000000681b86bd'))->getString()
);
$this->assertEquals(
'2400:cb00:2048:1::681f:5e2a',
IPAddress::fromRaw(hex2bin('2400cb002048000100000000681f5e2a'))->getString()
);
}
public function testCompare()
{
$v4_start = IPAddress::fromString('117.0.0.255');
$v4_end = IPAddress::fromString('127.0.0.255');
$v6_start = IPAddress::fromString('::1');
$v6_end = IPAddress::fromString('::FFFF');
$this->assertEquals(1, $v4_start->compareTo($v4_end));
$this->assertEquals(-1, $v4_end->compareTo($v4_start));
$this->assertEquals(0, $v4_start->compareTo($v4_start));
$this->assertEquals(0, $v4_end->compareTo($v4_end));
$this->assertEquals(1, $v6_start->compareTo($v6_end));
$this->assertEquals(-1, $v6_end->compareTo($v6_start));
$this->assertEquals(0, $v6_start->compareTo($v6_start));
$this->assertEquals(0, $v6_end->compareTo($v6_end));
}
public function testMaskedRange()
{
$range_v4 = IPAddressRange::fromMaskedString('127.0.0.1/8');
$this->assertEquals('127.0.0.1', $range_v4->getMaskAddress()->getString());
$this->assertEquals(8, $range_v4->getCidrLength());
$this->assertEquals('127.0.0.1/8', $range_v4->getMaskedString());
$range_v6 = IPAddressRange::fromMaskedString('::1/16');
$this->assertEquals('::1', $range_v6->getMaskAddress()->getString());
$this->assertEquals(16, $range_v6->getCidrLength());
$this->assertEquals('::1/16', $range_v6->getMaskedString());
}
// excellent naming
public function testRangedRange()
{
$range_v4 = IPAddressRange::fromRangeString('255.255.255.248-255.255.255.255');
$this->assertEquals('255.255.255.248', $range_v4->getMaskAddress()->getString());
$this->assertEquals(29, $range_v4->getCidrLength());
$range_v6 = IPAddressRange::fromRangeString('2400:cb00:2048:1::681b:86bd-2400:cb00:2048:1::681f:5e2a');
$this->assertEquals('2400:cb00:2048:1::6818:0', $range_v6->getMaskAddress()->getString());
$this->assertEquals(109, $range_v6->getCidrLength());
}
public function testMatchRange()
{
$range_v4 = new IPAddressRange(IPAddress::fromString('108.162.192.0'), 18);
$this->assertTrue($range_v4->match(IPAddress::fromString('108.162.255.255')));
$this->assertFalse($range_v4->match(IPAddress::fromString('127.0.0.1')));
$range_v6 = new IPAddressRange(IPAddress::fromString('2a06:98c0::'), 29);
$this->assertTrue($range_v6->match(IPAddress::fromString('2a06:98c7:7f:43:645:ab:cd:2525')));
$this->assertFalse($range_v6->match(IPAddress::fromString('::1')));
}
}

View file

@ -1,77 +0,0 @@
<?php
namespace MisuzuTests;
use PHPUnit\Framework\TestCase;
use Misuzu\Net\IP;
class IPTest extends TestCase
{
public function testVersion()
{
$this->assertEquals(IP::version('127.0.0.1'), IP::V4);
$this->assertEquals(IP::version('104.27.135.189'), IP::V4);
$this->assertEquals(IP::version('104.27.154.200'), IP::V4);
$this->assertEquals(IP::version('104.28.9.4'), IP::V4);
$this->assertEquals(IP::version('::1'), IP::V6);
$this->assertEquals(IP::version('2400:cb00:2048:1:0:0:681b:9ac8'), IP::V6);
$this->assertEquals(IP::version('2400:cb00:2048:1:0:0:681c:804'), IP::V6);
$this->assertEquals(IP::version('2400:cb00:2048:1:0:0:681b:86bd'), IP::V6);
$this->assertEquals(IP::version('2400:cb00:2048:1:0:0:681f:5e2a'), IP::V6);
$this->assertEquals(IP::version('not an ip address'), 0);
$this->assertEquals(IP::version('256.256.256.256'), 0);
}
public function testUnpack()
{
$this->assertEquals(IP::unpack('127.0.0.1'), "\x7f\x00\x00\x01");
$this->assertEquals(IP::unpack('104.27.135.189'), "\x68\x1b\x87\xbd");
$this->assertEquals(IP::unpack('104.27.154.200'), "\x68\x1b\x9a\xc8");
$this->assertEquals(IP::unpack('104.28.9.4'), "\x68\x1c\x09\x04");
$this->assertEquals(IP::unpack('::1'), "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01");
$this->assertEquals(
IP::unpack('2400:cb00:2048:1:0:0:681b:9ac8'),
"\x24\x00\xcb\x00\x20\x48\x00\x01\x00\x00\x00\x00\x68\x1b\x9a\xc8"
);
$this->assertEquals(
IP::unpack('2400:cb00:2048:1:0:0:681c:804'),
"\x24\x00\xcb\x00\x20\x48\x00\x01\x00\x00\x00\x00\x68\x1c\x08\x04"
);
$this->assertEquals(
IP::unpack('2400:cb00:2048:1:0:0:681b:86bd'),
"\x24\x00\xcb\x00\x20\x48\x00\x01\x00\x00\x00\x00\x68\x1b\x86\xbd"
);
$this->assertEquals(
IP::unpack('2400:cb00:2048:1:0:0:681f:5e2a'),
"\x24\x00\xcb\x00\x20\x48\x00\x01\x00\x00\x00\x00\x68\x1f\x5e\x2a"
);
}
public function testPack()
{
$this->assertEquals(IP::pack("\x7f\x00\x00\x01"), '127.0.0.1');
$this->assertEquals(IP::pack("\x68\x1b\x87\xbd"), '104.27.135.189');
$this->assertEquals(IP::pack("\x68\x1b\x9a\xc8"), '104.27.154.200');
$this->assertEquals(IP::pack("\x68\x1c\x09\x04"), '104.28.9.4');
$this->assertEquals(IP::pack("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"), '::1');
$this->assertEquals(
IP::pack("\x24\x00\xcb\x00\x20\x48\x00\x01\x00\x00\x00\x00\x68\x1b\x9a\xc8"),
'2400:cb00:2048:1::681b:9ac8'
);
$this->assertEquals(
IP::pack("\x24\x00\xcb\x00\x20\x48\x00\x01\x00\x00\x00\x00\x68\x1c\x08\x04"),
'2400:cb00:2048:1::681c:804'
);
$this->assertEquals(
IP::pack("\x24\x00\xcb\x00\x20\x48\x00\x01\x00\x00\x00\x00\x68\x1b\x86\xbd"),
'2400:cb00:2048:1::681b:86bd'
);
$this->assertEquals(
IP::pack("\x24\x00\xcb\x00\x20\x48\x00\x01\x00\x00\x00\x00\x68\x1f\x5e\x2a"),
'2400:cb00:2048:1::681f:5e2a'
);
}
}