From e12f7c516d5581bdfef586768b41fea01c164a7c Mon Sep 17 00:00:00 2001 From: Dominique Fournier Date: Wed, 10 Jan 2018 14:34:35 +0000 Subject: [PATCH] * ipaddresses : add validCIDR, validIPv4CIDR, validIPv6CIDR to check the CIDR * ipaddresses : add compressIP to compress an IPv6 address (remove the 0 and add the :: if possible) * ipaddresses : add networkFirstIP and networkLastIP to get the first and last address of a network. In IPv4, the first address is network address and the last address is broadcast address. * ipaddresses : add ipInNetwork to know if the provided ip address is in or outside the network and cidr provided git-svn-id: https://svn.fournier38.fr/svn/ProgSVN/trunk@4026 bf3deb0d-5f1a-0410-827f-c0cc1f45334c --- Tests/ipaddressesTest.php | 247 ++++++++++++++++++++++++++++++++++- ipaddresses.php | 263 +++++++++++++++++++++++++++++++++++++- 2 files changed, 507 insertions(+), 3 deletions(-) diff --git a/Tests/ipaddressesTest.php b/Tests/ipaddressesTest.php index 7d3c428..ec13193 100644 --- a/Tests/ipaddressesTest.php +++ b/Tests/ipaddressesTest.php @@ -6,7 +6,6 @@ /** Test the ipaddresses.php file */ class test_ipaddresses extends PHPUnit_Framework_TestCase { - public function test_validIPAddress1 () { $i = new ipaddresses (); @@ -100,6 +99,118 @@ class test_ipaddresses extends PHPUnit_Framework_TestCase $this->assertSame (false, $res); } + public function test_validCIDR1 () + { + $i = new ipaddresses (); + $res = $i->validCIDR (-1); + $this->assertSame (false, $res); + } + public function test_validCIDR2 () + { + $i = new ipaddresses (); + $res = $i->validCIDR (129); + $this->assertSame (false, $res); + } + public function test_validCIDR3 () + { + $i = new ipaddresses (); + $res = $i->validCIDR (128); + $this->assertSame (true, $res); + } + public function test_validCIDR4 () + { + $i = new ipaddresses (); + $res = $i->validCIDR (0); + $this->assertSame (true, $res); + } + + public function test_validIPv4CIDR1 () + { + $i = new ipaddresses (); + $res = $i->validIPv4CIDR (-1); + $this->assertSame (false, $res); + } + public function test_validIPv4CIDR2 () + { + $i = new ipaddresses (); + $res = $i->validIPv4CIDR (33); + $this->assertSame (false, $res); + } + public function test_validIPv4CIDR3 () + { + $i = new ipaddresses (); + $res = $i->validIPv4CIDR (32); + $this->assertSame (true, $res); + } + public function test_validIPv4CIDR4 () + { + $i = new ipaddresses (); + $res = $i->validIPv4CIDR (0); + $this->assertSame (true, $res); + } + + public function test_validIPv6CIDR1 () + { + $i = new ipaddresses (); + $res = $i->validIPv6CIDR (-1); + $this->assertSame (false, $res); + } + public function test_validIPv6CIDR2 () + { + $i = new ipaddresses (); + $res = $i->validIPv6CIDR (129); + $this->assertSame (false, $res); + } + public function test_validIPv6CIDR3 () + { + $i = new ipaddresses (); + $res = $i->validIPv6CIDR (128); + $this->assertSame (true, $res); + } + public function test_validIPv6CIDR4 () + { + $i = new ipaddresses (); + $res = $i->validIPv6CIDR (0); + $this->assertSame (true, $res); + } + + public function test_compressIP1 () + { + $i = new ipaddresses (); + $res = $i->compressIP ("::"); + $this->assertSame ("::", $res); + } + public function test_compressIP2 () + { + $i = new ipaddresses (); + $res = $i->compressIP ("::1"); + $this->assertSame ("::1", $res); + } + public function test_compressIP3 () + { + $i = new ipaddresses (); + $res = $i->compressIP ("2::1"); + $this->assertSame ("2::1", $res); + } + public function test_compressIP4 () + { + $i = new ipaddresses (); + $res = $i->compressIP ("2::"); + $this->assertSame ("2::", $res); + } + public function test_compressIP5 () + { + $i = new ipaddresses (); + $res = $i->compressIP ("2:1:0:3::0:1"); + $this->assertSame ("2:1:0:3::1", $res); + } + public function test_compressIP6 () + { + $i = new ipaddresses (); + $res = $i->compressIP ("2:1:0:3:0000::1"); + $this->assertSame ("2:1:0:3::1", $res); + } + public function test_uncompressIPv6a () { $i = new ipaddresses (); @@ -252,4 +363,138 @@ class test_ipaddresses extends PHPUnit_Framework_TestCase $res = $i->netmask2cidr ("63.0.0.0"); $this->assertSame (6, $res); } + + public function test_ipInNetwork1 () + { + $i = new ipaddresses (); + $res = $i->ipInNetwork ("127.0.0.1", "127.1.0.0", 8); + $this->assertSame (true, $res); + } + public function test_ipInNetwork2 () + { + $i = new ipaddresses (); + $res = $i->ipInNetwork ("192.168.1.1", "127.1.0.0", 8); + $this->assertSame (false, $res); + } + public function test_ipInNetwork3 () + { + $i = new ipaddresses (); + $res = $i->ipInNetwork ("2001:660:530d:201::1", "2001:660:530d:201::", 64); + $this->assertSame (true, $res); + } + public function test_ipInNetwork4 () + { + $i = new ipaddresses (); + $res = $i->ipInNetwork ("2001:660:530d:203::1", "2001:660:530d:201::", 64); + $this->assertSame (false, $res); + } + public function test_ipInNetwork5 () + { + $i = new ipaddresses (); + $res = $i->ipInNetwork ("2001:660:530d:203::1", "2001::", 0); + $this->assertSame (true, $res); + } + public function test_ipInNetwork6 () + { + $i = new ipaddresses (); + $res = $i->ipInNetwork ("192.168.1.1", "127.0.0.0", 0); + $this->assertSame (true, $res); + } + + public function test_networkFirstIP1 () + { + $i = new ipaddresses (); + $res = $i->networkFirstIP ("192.168.1.21", 24); + $this->assertSame ($res, "192.168.1.0"); + } + public function test_networkFirstIP2 () + { + $i = new ipaddresses (); + $res = $i->networkFirstIP ("192.168.1.21", 0); + $this->assertSame ($res, "0.0.0.0"); + } + public function test_networkFirstIP3 () + { + $i = new ipaddresses (); + $res = $i->networkFirstIP ("192.168.1.21", 32); + $this->assertSame ($res, "192.168.1.21"); + } + public function test_networkFirstIP4 () + { + $i = new ipaddresses (); + $res = $i->networkFirstIP ("192.168.1.21", 31); + $this->assertSame ($res, "192.168.1.20"); + } + public function test_networkFirstIP5 () + { + $i = new ipaddresses (); + $res = $i->networkFirstIP ("2001:660:530d:201::125", 64); + $this->assertSame ($res, "2001:660:530d:201::"); + } + public function test_networkFirstIP6 () + { + $i = new ipaddresses (); + $res = $i->networkFirstIP ("2001:660:530d:201::125", 0); + $this->assertSame ($res, "::"); + } + public function test_networkFirstIP7 () + { + $i = new ipaddresses (); + $res = $i->networkFirstIP ("2001:660:530d:201::125", 128); + $this->assertSame ($res, "2001:660:530d:201::125"); + } + public function test_networkFirstIP8 () + { + $i = new ipaddresses (); + $res = $i->networkFirstIP ("2001:660:530d:201::125", 127); + $this->assertSame ($res, "2001:660:530d:201::124"); + } + public function test_networkLastIP1 () + { + $i = new ipaddresses (); + $res = $i->networkLastIP ("192.168.1.21", 24); + $this->assertSame ($res, "192.168.1.255"); + } + public function test_networkLastIP2 () + { + $i = new ipaddresses (); + $res = $i->networkLastIP ("192.168.1.21", 0); + $this->assertSame ($res, "255.255.255.255"); + } + public function test_networkLastIP3 () + { + $i = new ipaddresses (); + $res = $i->networkLastIP ("192.168.1.21", 32); + $this->assertSame ($res, "192.168.1.21"); + } + public function test_networkLastIP4 () + { + $i = new ipaddresses (); + $res = $i->networkLastIP ("192.168.1.21", 31); + $this->assertSame ($res, "192.168.1.21"); + } + public function test_networkLastIP5 () + { + $i = new ipaddresses (); + $res = $i->networkLastIP ("2001:660:530d:201::125", 64); + $this->assertSame ($res, "2001:660:530d:201:ffff:ffff:ffff:ffff"); + } + public function test_networkLastIP6 () + { + $i = new ipaddresses (); + $res = $i->networkLastIP ("2001:660:530d:201::125", 0); + $this->assertSame ($res, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"); + } + public function test_networkLastIP7 () + { + $i = new ipaddresses (); + $res = $i->networkLastIP ("2001:660:530d:201::125", 128); + $this->assertSame ($res, "2001:660:530d:201::125"); + } + public function test_networkLastIP8 () + { + $i = new ipaddresses (); + $res = $i->networkLastIP ("2001:660:530d:201::125", 127); + $this->assertSame ($res, "2001:660:530d:201::125"); + } } diff --git a/ipaddresses.php b/ipaddresses.php index 23e72f2..e9084b8 100644 --- a/ipaddresses.php +++ b/ipaddresses.php @@ -49,18 +49,125 @@ class ipaddresses return TRUE; } + /** Return true if the provided CIDR is valid. The CIDR can be valid in IPv4 + * or IPv6 + * @param integer $cidr The CIDR to test + * @return boolean The CIDR is valid + */ + public function validCIDR ($cidr) + { + if (! is_integer ($cidr)) + throw new \Exception (dgettext("domframework", "Invalid CIDR provided"), + 500); + if ($cidr < 0 || $cidr > 128) + return false; + return true; + } + + /** Return true if the provided CIDR is valid. The CIDR can be valid in IPv4. + * @param integer $cidr The CIDR to test + * @return boolean The CIDR is valid + */ + public function validIPv4CIDR ($cidr) + { + if (! is_integer ($cidr)) + throw new \Exception (dgettext("domframework", "Invalid CIDR provided"), + 500); + if ($cidr < 0 || $cidr > 32) + return false; + return true; + } + + /** Return true if the provided CIDR is valid. The CIDR can be valid in IPv6. + * @param integer $cidr The CIDR to test + * @return boolean The CIDR is valid + */ + public function validIPv6CIDR ($cidr) + { + if (! is_integer ($cidr)) + throw new \Exception (dgettext("domframework", "Invalid CIDR provided"), + 500); + if ($cidr < 0 || $cidr > 128) + return false; + return true; + } + + /** Return the IPv6 to compressed form. + * Remove the 0 when they are placed on the begin of the nibble. + * Remove all the blocks only zero to convert them to :: (but only one time) + * Example: 2001:0660:530d:0201:0000:0000:0000:0124 => 2001:660:530d:201::124 + * If an IPv4 is provided, return it without modification + */ + public function compressIP ($ip) + { + if (strpos ($ip, ":") === false) + return $ip; + $ip = $this->uncompressIPv6 ($ip); + $ipArr = explode (":", $ip); + if (count ($ipArr) !== 8) + return $ip; + // Remove the 0 if they are at the beginning of the nibble + foreach ($ipArr as &$ip) + { + $ip = sprintf ("%x", hexdec ($ip)); + } + // Remove the 0 nibble if there is an other 0 nibble after + $cleanLoop = false; + $ipCompArr = array (); + foreach ($ipArr as $key=>$val) + { + // echo "VAL=".var_export ($val, true). + // ", cleanLoop=".var_export ($cleanLoop,true); + if ($val !== "0") + { + // echo " => In NOT zero\n"; + if ($cleanLoop) + $cleanLoop = false; + } + elseif($cleanLoop) + { + // echo " => In CleanLoop\n"; + unset ($ipArr[$key]); + } + else + { + // echo " => In ZERO\n"; + if (key_exists ($key+1, $ipArr) && $ipArr[$key+1] === "0") + { + // echo " ===> NEXT ZERO : CleanLoop Start\n"; + $cleanLoop = true; + $ipArr[$key] = ""; + } + } + } + $ipArr = array_values ($ipArr); + if (end ($ipArr) === "") + { + array_pop ($ipArr); + $ipArr[] = ":"; + } + if (reset ($ipArr) === "") + $ipArr[0] = ":"; + if (count ($ipArr) === 1) + $ipArr[] = ""; + $ipHex = implode (":", $ipArr); + return $ipHex; + } + /** Return the IPv6 uncompressed (all the fields exists). They are not filled * by zeros * If the provided IP is IPv4, there is no change applied * Return False if the parameter is invalid * Based on http://www.weberdev.com/get_example.php3?ExampleID=3921 * @param string $ip The IP address to uncompress + * Example : $ip="::" => return "0:0:0:0:0:0:0:0" */ public function uncompressIPv6 ($ip) { - if (! is_string ($ip) || $ip === "") + if (! is_string ($ip) || $ip === "" || + $this->validIPAddress ($ip) === false) throw new \Exception (dgettext("domframework", "Invalid IP address"), - 500); + 500); if (strstr ($ip,"::")) { $e = explode (":", $ip); @@ -84,6 +191,8 @@ class ipaddresses } } $ip = implode (":", $newipv6); + if (substr_count ($ip, ":") !== 7) + throw new \Exception ("uncompressIPv6: Invalid IP provided", 500); } return $ip; } @@ -120,6 +229,8 @@ class ipaddresses * Valid only on IPv6 (but don't change anything if the provided address is * IPv4) * @param string $ip The IP to complete + * @return string The address in nibbles + * Example : $ip = "::", return "0000:0000:0000:0000:0000:0000:0000:0000" */ public function completeAddressWithZero ($ip) { @@ -273,4 +384,152 @@ class ipaddresses } return $i; } + + /** This function return true if the provided address is in the provided + * network with the associated cidr + * @param string $ip The IPv4 or IPv6 to test + * @param string $network The IPv4 or IPv6 network base + * @param string $cidr The CIDR to apply + * @return boolean True if $ip is in $network/$cidr + */ + public function ipInNetwork ($ip, $network, $cidr) + { + if ($this->validIPAddress ($ip) === false) + throw new \Excpetion (dgettext("domframework", "Invalid IP address"), + 500); + if ($this->validIPAddress ($network) === false) + throw new \Excpetion (dgettext("domframework", "Invalid Network address"), + 500); + $ipv4 = $this->validIPv4Address ($ip); + $networkv4 = $this->validIPv4Address ($network); + if ($ipv4 !== $networkv4) + throw new \Exception (dgettext("domframework", + "Network and IP address are not compatible"), 500); + if ($ipv4 === true) + { + if ($this->validIPv4CIDR ($cidr) === false) + throw new \Exception (dgettext("domframework", + "CIDR is not IPv4 compatible"), 500); + } + else + { + if ($this->validIPv6CIDR ($cidr) === false) + throw new \Exception (dgettext("domframework", + "CIDR is not IPv6 compatible"), 500); + } + return ($this->networkFirstIP ($ip, $cidr) === + $this->networkFirstIP ($network, $cidr)); + } + + /** Get the first IP of a network. + * IPv4 and IPv6 compatible + * @param string $ip The IPv4 or IPv6 in the network + * @param string $cidr The CIDR to apply + * @return string the network base + * Example : $ip="192.168.1.2", $cidr=24 => return "192.168.1.0" + */ + public function networkFirstIP ($ip, $cidr) + { + return $this->networkFirstLastIP ($ip, $cidr, "0"); + } + + /** Get the last IP of a network. + * IPv4 and IPv6 compatible + * @param string $ip The IPv4 or IPv6 in the network + * @param string $cidr The CIDR to apply + * @return string the network base + * Example : $ip="192.168.1.2", $cidr=24 => return "192.168.1.255" + */ + public function networkLastIP ($ip, $cidr) + { + return $this->networkFirstLastIP ($ip, $cidr, "1"); + } + + /** Get the network first IP. + * IPv4 and IPv6 compatible + * @param string $ip The IPv4 or IPv6 in the network + * @param string $cidr The CIDR to apply + * @param string $map if "0", get the first address of the network, + * if "1" get the last address of the network + * @return string the network base + * Example : $ip="192.168.1.2", $cidr=24 => return "192.168.1.0" + */ + private function networkFirstLastIP ($ip, $cidr, $map) + { + if ($this->validIPAddress ($ip) === false) + throw new \Excpetion (dgettext("domframework", "Invalid IP address"), + 500); + $ipv4 = $this->validIPv4Address ($ip); + if ($ipv4 === true) + { + if ($this->validIPv4CIDR ($cidr) === false) + throw new \Exception (dgettext("domframework", + "CIDR is not IPv4 compatible"), 500); + } + else + { + if ($this->validIPv6CIDR ($cidr) === false) + throw new \Exception (dgettext("domframework", + "CIDR is not IPv6 compatible"), 500); + } + // Convert the IP and CIDR to binary string + if ($ipv4) + { + list ($ip1, $ip2, $ip3, $ip4) = explode (".", $ip); + $ipBin = sprintf ("%08b%08b%08b%08b", $ip1, $ip2, $ip3, $ip4); + $cidrBin = ""; + for ($i = 0 ; $i < $cidr ; $i++) + $cidrBin .= "1"; + for ($i = $cidr ; $i < 32 ; $i++) + $cidrBin .= "0"; + } + else + { + $ip = $this->completeAddressWithZero ($ip); + $ip = str_replace (":", "", $ip); + $ipArr = str_split ($ip, 2); + $ipBin = ""; + foreach ($ipArr as $ip) + $ipBin .= sprintf ("%08b", hexdec ($ip)); + $cidrBin = ""; + for ($i = 0 ; $i < $cidr ; $i++) + $cidrBin .= "1"; + for ($i = $cidr ; $i < 128 ; $i++) + $cidrBin .= "0"; + } + // Get the binary value of IP if the mask is 1 or put $map if the mask is 0 + $ipBaseBin = ""; + for ($i = 0 ; $i < strlen ($ipBin) ; $i++) + { + if ($cidrBin[$i] === "1") + $ipBaseBin .= $ipBin[$i]; + else + $ipBaseBin .= "$map"; + } + // Convert ipBaseBin from binary to decimal if IPv4 and to hexa for IPv6 + if ($ipv4) + { + $ipBase = ""; + for ($i = 0 ; $i < 32 ; $i = $i + 8) + { + $block = substr ($ipBaseBin, $i, 8); + if ($i > 0) + $ipBase .= "."; + $ipBase .= bindec ($block); + } + } + else + { + $ipBase = ""; + for ($i = 0 ; $i < 128 ; $i = $i + 8) + { + $block = substr ($ipBaseBin, $i, 8); + if ($i > 0 && $i % 16 == 0) + $ipBase .= ":"; + $ipBase .= sprintf ("%02x", bindec ($block)); + } + $ipBase = $this->compressIP ($ipBase); + } + return $ipBase; + } }