Implement IP (range) parsing

feature/node-rewrite
Sven Slootweg 12 years ago
parent 22206a9960
commit c85c90bcbd

@ -13,9 +13,277 @@
if(!isset($_CVM)) { die("Unauthorized."); }
define("IP_TYPE_NONE", 0);
define("IP_TYPE_IPV4", 4);
define("IP_TYPE_IPV6", 6);
define("INPUT_TYPE_IP", 1);
define("INPUT_TYPE_RANGE", 2);
define("SEGMENT_BITS_IPV4", 8);
define("SEGMENT_BITS_IPV6", 16);
class IpRange
{
public $sCidrNotation = "";
public $sType = 0;
public $sInputType = 0;
public $sInput = "";
public $sStart = "";
public $sEnd = "";
public $sCount = "";
public function __construct($input)
{
$this->sInput = $input;
$slashcount = substr_count($input, "/");
if($slashcount == 1)
{
/* The input is probably a CIDR range. */
$this->sInputType = INPUT_TYPE_RANGE;
$this->ValidateRangeFormat();
}
elseif($slashcount == 0)
{
/* The input is probably an IP. */
$this->sInputType = INPUT_TYPE_IP;
$this->ValidateIpFormat();
}
else
{
throw new InvalidArgumentException("The given input is not a valid IP or IP range.");
}
if($this->sInputType == INPUT_TYPE_RANGE)
{
if($this->sType == IP_TYPE_IPV6)
{
$this->ExpandIpv6();
$result = IpRange::ParseRange($this->sIp, $this->sSize, ":", SEGMENT_BITS_IPV6, true);
}
elseif($this->sType == IP_TYPE_IPV4)
{
$result = IpRange::ParseRange($this->sIp, $this->sSize);
}
$this->sStart = $result['start'];
$this->sEnd = $result['end'];
$this->sCount = $result['count'];
}
else
{
$this->sStart = $this->sIp;
$this->sEnd = $this->sIp;
$this->sCount = 1;
}
}
/* if($this->ValidateRangeFormat($input) === true)
{
list($ip, $size) = explode("/", $input, 2);
if($this->sType == IP_TYPE_IPV6)
{
$ip = $this->ExpandIpv6($ip);
}
$result = IpRange::ParseRange($ip, $size, ":", SEGMENT_BITS_IPV6, true);
echo "Start: {$result['start']} End: {$result['end']} Size: {$result['count']}";
}
else
{
throw new InvalidArgumentException("The given input is not a valid IP range.");
}
*
*
* if($this->ValidateIpFormat($input) === true)
{
}
else
{
throw new InvalidArgumentException("The given input is not a valid IP.");
}*/
private function ValidateRangeFormat()
{
list($ip, $size) = explode("/", $this->sInput, 2);
if($this->ValidateIpFormat($ip) === true)
{
if(is_numeric($size))
{
if($this->sType == IP_TYPE_IPV4 && (int)$size >= 0 && (int)$size <= 32)
{
$this->sSize = $size;
return true;
}
elseif($this->sType == IP_TYPE_IPV6 && (int)$size >= 0 && (int)$size <= 128)
{
$this->sSize = $size;
return true;
}
}
}
/* Fallback case. */
throw new InvalidArgumentException("The given input is not a valid IP or IP range.");
}
private function ValidateIpFormat($ip = null)
{
if(is_null($ip))
{
$ip = $this->sInput;
}
if(strpos($ip, ".") !== false)
{
// Probably an IPv4 address.
if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4))
{
$this->sType = IP_TYPE_IPV4;
$this->sIp = $ip;
return true;
}
}
elseif(strpos($ip, ":") !== false)
{
// Probably an IPv6 address.
if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6))
{
$this->sType = IP_TYPE_IPV6;
$this->sIp = $ip;
return true;
}
}
/* Fallback case. */
throw new InvalidArgumentException("The given input is not a valid IP or IP range.");
}
private function ExpandIpv6()
{
// Note: this function does NOT do any validation!
$ip = $this->sIp;
$parts = explode(":", $ip);
$empty_part = false;
$missing = 0;
foreach($parts as $part)
{
if($part == "")
{
$empty_part = true;
$missing = 1;
}
}
$available_parts = count($parts) - $missing;
if($available_parts < 8 || $empty_part === true)
{
$needed = 8 - $available_parts;
$abbrev_position = strpos($ip, "::");
$left = substr($ip, 0, $abbrev_position);
$right = substr($ip, $abbrev_position + 2);
$left_parts = explode(":", $left);
$right_parts = explode(":", $right);
$filler_parts = array_fill(0, $needed, "0000");
$final_array = array_merge($left_parts, $filler_parts);
$final_array = array_merge($final_array, $right_parts);
}
else
{
$final_array = $parts;
}
foreach($final_array as &$part)
{
$part = str_pad($part, 4, "0", STR_PAD_LEFT);
}
$this->sIp = implode(":", $final_array);
return true;
}
public static function ParseRange($start, $size, $delimiter = ".", $segment_bits = 8, $hex = false)
{
$segments = explode($delimiter, $start);
/* Determine the maximum size of one segment */
$segment_size = pow(2, $segment_bits);
/* Calculate the amount of bits in the entire IP address */
$ip_bits = count($segments) * $segment_bits;
/* Calculate the total amount of IPs possible for this IP format */
$total_ips = pow(2, $ip_bits - $size);
if($hex === true)
{
$segments = array_map("hexdec", $segments);
}
/* Calculate the maximum size (in IPs) of the currently used IP format */
$max_size = count($segments) * $segment_bits;
/* Determine what segment we are going to modify */
$applicable_segment = floor($size / $segment_bits) + 1;
/* Ensure that the specified size is possible */
if($size > $max_size)
{
/* The size exceeds the total space for this type of IP */
return false;
}
elseif($size == $max_size)
{
/* Only 1 IP. */
$start_segments = $segments;
}
else
{
/* Determine the amount of IPs for the given size */
$class_size = pow(2, ($applicable_segment * $segment_bits) - $size);
/* Round down the applicable segment if necessary to ensure the starting point is valid */
$segments[$applicable_segment - 1] = floor($segments[$applicable_segment - 1] / $class_size) * $class_size;
$start_segments = $segments;
/* Add the amount of IPs (inclusive) to the applicable segment */
$segments[$applicable_segment - 1] += $class_size - 1;
/* Set all segments to the right of the applicable segment to the right value. */
if(count($segments) > $applicable_segment - 1)
{
for($i = $applicable_segment + 1; $i <= count($segments); $i++)
{
$segments[$i - 1] = $segment_size - 1;
$start_segments[$i - 1] = 0;
}
}
}
if($hex === true)
{
$segments = array_map("dechex", $segments);
$start_segments = array_map("dechex", $start_segments);
}
return array(
'start' => implode($delimiter, $start_segments),
'end' => implode($delimiter, $segments),
'count' => $total_ips
);
}
}
?>

@ -25,4 +25,5 @@ require("classes/class.container.php");
require("classes/class.node.php");
require("classes/class.template.php");
require("classes/class.sshconnector.php");
require("classes/class.iprange.php");
?>

@ -0,0 +1,42 @@
<?php
function test_iprange($input, $desired_start, $desired_end, $desired_size, $desired_type)
{
try
{
$obj = new IpRange($input);
if($obj->sStart != $desired_start)
{
echo("IpRange unit test failed due to sStart mismatch. Expected: {$desired_start} &nbsp;&nbsp; Actual: {$obj->sStart}<br>");
}
if($obj->sEnd != $desired_end)
{
echo("IpRange unit test failed due to sEnd mismatch. Expected: {$desired_end} &nbsp;&nbsp; Actual: {$obj->sEnd}<br>");
}
if($obj->sSize != $desired_size)
{
echo("IpRange unit test failed due to sSize mismatch. Expected: {$desired_size} &nbsp;&nbsp; Actual: {$obj->sSize}<br>");
}
if($obj->sType != $desired_type)
{
echo("IpRange unit test failed due to sType mismatch. Expected: {$desired_type} &nbsp;&nbsp; Actual: {$obj->sType}<br>");
}
}
catch (Exception $e)
{
echo("IpRange unit test failed due to exception! Input: {$input} &nbsp;&nbsp; Error message: " . $e->getMessage() . "<br>");
}
}
test_iprange("fe80:0000:0000:0000:e0d3:f0ff:fe28:5f47/64", "fe80:0:0:0:0:0:0:0", "fe80:0:0:0:ffff:ffff:ffff:ffff", 64, 6);
test_iprange("fe80:0000:0000:0000:e0d3:f0ff:fe28:5f47", "fe80:0000:0000:0000:e0d3:f0ff:fe28:5f47", "fe80:0000:0000:0000:e0d3:f0ff:fe28:5f47", 0, 6);
test_iprange("0.0.0.0/1", "0.0.0.0", "127.255.255.255", 1, 4);
test_iprange("162.16.47.0/16", "162.16.0.0", "162.16.255.255", 16, 4);
test_iprange("192.168.1.0/27", "192.168.1.0", "192.168.1.31", 27, 4);
test_iprange("192.168.1.0/32", "192.168.1.0", "192.168.1.0", 32, 4);
test_iprange("192.168.1.0", "192.168.1.0", "192.168.1.0", 0, 4);

@ -58,7 +58,8 @@ try
'^/login/?$' => "module.login.php",
'^/logout/?$' => "module.logout.php",
'^/admin(/.*)?$' => "module.admin.php",
'^/([0-9]+)(/.*)?$' => "module.vps.php"
'^/([0-9]+)(/.*)?$' => "module.vps.php",
'^/test/?$' => "module.test.php"
)
);

Loading…
Cancel
Save