--- /etc/inc/util.inc.orig 2010-03-09 13:01:37.000000000 -0500 +++ /etc/inc/util.inc 2010-03-09 13:01:40.000000000 -0500 @@ -78,6 +78,127 @@ return long2ip(gen_subnet_mask_long($bits)); } +/* Convert IP address to unsigned long int. */ +function ip2ulong($ip) { + return sprintf("%u", ip2long($ip)); +} + +/* Find out how many IPs are contained within a given IP range + * e.g. 192.168.0.0 to 192.168.0.255 returns 256 + */ +function ip_range_size($startip, $endip) { + if (is_ipaddr($startip) && is_ipaddr($endip)) { + // Operate as unsigned long because otherwise it wouldn't work + // when crossing over from 127.255.255.255 / 128.0.0.0 barrier + return abs(ip2ulong($startip) - ip2ulong($endip)) + 1; + } + return -1; +} + +/* Find the smallest possible subnet mask which can contain a given number of IPs + * e.g. 512 IPs can fit in a /23, but 513 IPs need a /22 + */ +function find_smallest_cidr($number) { + $smallest = 1; + for ($b=32; $b > 0; $b--) { + $smallest = ($number <= pow(2,$b)) ? $b : $smallest; + } + return (32-$smallest); +} + +/* Return the previous IP address before the given address */ +function ip_before($ip) { + return long2ip(ip2long($ip)-1); +} + +/* Return the next IP address after the given address */ +function ip_after($ip) { + return long2ip(ip2long($ip)+1); +} + +/* Return true if the first IP is 'before' the second */ +function ip_less_than($ip1, $ip2) { + // Compare as unsigned long because otherwise it wouldn't work when + // crossing over from 127.255.255.255 / 128.0.0.0 barrier + return ip2ulong($ip1) < ip2ulong($ip2); +} + +/* Return true if the first IP is 'after' the second */ +function ip_greater_than($ip1, $ip2) { + // Compare as unsigned long because otherwise it wouldn't work + // when crossing over from 127.255.255.255 / 128.0.0.0 barrier + return ip2ulong($ip1) > ip2ulong($ip2); +} + +/* Convert a range of IPs to an array of subnets which can contain the range. */ +function ip_range_to_subnet_array($startip, $endip) { + if (!is_ipaddr($startip) || !is_ipaddr($endip)) { + return array(); + } + + // Container for subnets within this range. + $rangesubnets = array(); + + // Figure out what the smallest subnet is that holds the number of IPs in the given range. + $cidr = find_smallest_cidr(ip_range_size($startip, $endip)); + + // Loop here to reduce subnet size and retest as needed. We need to make sure + // that the target subnet is wholly contained between $startip and $endip. + for ($cidr; $cidr <= 32; $cidr++) { + // Find the network and broadcast addresses for the subnet being tested. + $targetsub_min = gen_subnet($startip, $cidr); + $targetsub_max = gen_subnet_max($startip, $cidr); + + // Check best case where the range is exactly one subnet. + if (($targetsub_min == $startip) && ($targetsub_max == $endip)) { + // Hooray, the range is exactly this subnet! + return array("{$startip}/{$cidr}"); + } + + // These remaining scenarios will find a subnet that uses the largest + // chunk possible of the range being tested, and leave the rest to be + // tested recursively after the loop. + + // Check if the subnet begins with $startip and ends before $endip + if (($targetsub_min == $startip) && ip_less_than($targetsub_max, $endip)) { + break; + } + + // Check if the subnet ends at $endip and starts after $startip + if (ip_greater_than($targetsub_min, $startip) && ($targetsub_max == $endip)) { + break; + } + + // Check if the subnet is between $startip and $endip + if (ip_greater_than($targetsub_min, $startip) && ip_less_than($targetsub_max, $endip)) { + break; + } + } + + // Some logic that will recursivly search from $startip to the first IP before the start of the subnet we just found. + // NOTE: This may never be hit, the way the above algo turned out, but is left for completeness. + if ($startip != $targetsub_min) { + $rangesubnets = array_merge($rangesubnets, ip_range_to_subnet_array($startip, ip_before($targetsub_min))); + } + + // Add in the subnet we found before, to preserve ordering + $rangesubnets[] = "{$targetsub_min}/{$cidr}"; + + // And some more logic that will search after the subnet we found to fill in to the end of the range. + if ($endip != $targetsub_max) { + $rangesubnets = array_merge($rangesubnets, ip_range_to_subnet_array(ip_after($targetsub_max), $endip)); + } + return $rangesubnets; +} + +function is_iprange($range) { + if (substr_count($range, '-') != 1) { + return false; + } + list($ip1, $ip2) = explode ('-', $range); + return (is_ipaddr($ip1) && is_ipaddr($ip2)); +} + function is_numericint($arg) { return (preg_match("/[^0-9]/", $arg) ? false : true); } --- /usr/local/www/firewall_aliases_edit.php.orig 2010-03-09 13:08:12.000000000 -0500 +++ /usr/local/www/firewall_aliases_edit.php 2010-03-10 15:49:57.000000000 -0500 @@ -96,11 +96,6 @@ $reqdfields = explode(" ", "name address"); $reqdfieldsn = explode(",", "Name,Address"); - if ($_POST['type'] == "network") { - $reqdfields[] = "address_subnet"; - $reqdfieldsn[] = "Subnet bit count"; - } - do_input_validation($_POST, $reqdfields, $reqdfieldsn, &$input_errors); if(strtolower($_POST['name']) == "lan") @@ -122,10 +117,13 @@ $input_errors[] = "A valid address must be specified."; } if ($_POST['type'] == "network") { - if (!is_ipaddr($_POST['address'])) { + if (!is_numeric($_POST['address_subnet']) && !is_iprange($_POST['address'])) { + $input_errors[] = "Subnet bit count must be specified"; + } + if (!is_ipaddr($_POST['address']) && !is_iprange($_POST['address'])) { $input_errors[] = "A valid address must be specified."; } - if (!is_numeric($_POST['address_subnet'])) { + if (!is_numeric($_POST['address_subnet']) && !is_iprange($_POST['address'])) { $input_errors[] = "A valid subnet bit count must be specified."; } } @@ -160,21 +158,28 @@ $alias = array(); $alias['name'] = $_POST['name']; - if ($_POST['type'] == "network") - $alias['address'] = $_POST['address'] . "/" . $_POST['address_subnet']; - else + $count = 1; + if ($_POST['type'] == "network") { + if (is_iprange($_POST['address'])) { + list($startip, $endip) = explode('-', $_POST["address"]); + $rangesubnets = ip_range_to_subnet_array($startip, $endip); + $count = count($rangesubnets); + $alias['address'] .= implode($rangesubnets, ' '); + } else { + $alias['address'] = $_POST['address'] . "/" . $_POST['address_subnet']; + } + } else { $alias['address'] = $_POST['address']; + } $address = $alias['address']; $final_address_detail = mb_convert_encoding($_POST['detail'],"HTML-ENTITIES","auto"); - if($final_address_detail <> "") { - $final_address_details .= $final_address_detail; + if($final_address_detail <> "") { + $final_address_details .= str_repeat($final_address_detail . "||", $count); } else { - $final_address_details .= "Entry added" . " "; - $final_address_details .= date('r'); - } - $final_address_details .= "||"; + $final_address_details .= str_repeat("Entry added " . date('r') . "||", $count); + } $isfirst = 0; if($_POST['type'] == "url") { @@ -234,28 +239,38 @@ } else { /* item is a normal alias type */ for($x=0; $x<299; $x++) { + $count = 1; $comd = "\$subnet = \$_POST['address" . $x . "'];"; eval($comd); $comd = "\$subnet_address = \$_POST['address_subnet" . $x . "'];"; eval($comd); if($subnet <> "") { - $address .= " "; - $address .= $subnet; - if($subnet_address <> "") $address .= "/" . $subnet_address; + if ($_POST['type'] == "network" && is_iprange($subnet)) { + list($startip, $endip) = explode('-', $subnet); + $rangesubnets = ip_range_to_subnet_array($startip, $endip); + $count = count($rangesubnets); + if ($address <> "") { + $address .= " "; + } + $address .= implode($rangesubnets, ' '); + } else { + $address .= " " . $subnet; + if ($subnet_address <> "") { + $address .= "/" . $subnet_address; + } + } /* Compress in details to a single key, data separated by pipes. Pulling details here lets us only pull in details for valid address entries, saving us from having to track which ones to process later. */ - $comd = "\$final_address_detail = mb_convert_encoding(\$_POST['detail" . $x . "'],'HTML-ENTITIES','auto');"; - eval($comd); - if($final_address_detail <> "") { - $final_address_details .= $final_address_detail; - } else { - $final_address_details .= "Entry added" . " "; - $final_address_details .= date('r'); - } - $final_address_details .= "||"; + $comd = "\$final_address_detail = mb_convert_encoding(\$_POST['detail" . $x . "'],'HTML-ENTITIES','auto');"; + eval($comd); + if($final_address_detail <> "") { + $final_address_details .= str_repeat($final_address_detail . "||", $count); + } else { + $final_address_details .= str_repeat("Entry added " . date('r') . "||", $count); + } } } }