--- /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 18:54:36.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);
+				}
 			}
 		}
 	}
@@ -358,7 +373,7 @@
 $url_str = gettext("URL");
 $update_freq_str = gettext("Update Freq.");
 
-$networks_help = gettext("Networks can be expressed like 10.0.0.0 format.  Select the CIDR (network mask) that pertains to each entry.");
+$networks_help = gettext("Networks can be expressed like 10.0.0.0 format.  Select the CIDR (network mask) that pertains to each entry. You may also enter an IP Range such as 192.168.1.1-192.168.1.254.");
 $hosts_help = gettext("Enter as many hosts as you would like.  Hosts should be expressed in their ip address format.");
 $ports_help = gettext("Enter as many ports as you wish.  Port ranges can be expressed by seperating with a colon.");
 $url_help = gettext("Enter as many urls as you wish.  Also set the time that you would like the url refreshed in days.  After saving {$g['product_name']} will download the URL and import the items into the alias.");
@@ -595,4 +610,4 @@
 	}
 	fclose($fd);
 }
-?>
\ No newline at end of file
+?>